Tokitsukaze and Rescue–hdu 6797
传送门
思路
- 对最多5个边进行删除操作,即将边权值设置成 i n f inf inf即可
- 对于每一次的删边操作,枚举每一次跑的最短路径中的 n − 1 n-1 n−1条边
- 最后在删了k次边后再跑一遍 d i j k s t r a dijkstra dijkstra
- 由于边比较小,直接使用邻接矩阵 f o r for for循环跑一遍即可,身边有人用链式向前星会莫名超时,我也不懂,8s是绰绰有余的
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
inline void read(T &x)
{
int tmp = 1;char c = getchar();x = 0;
while (c > '9' || c < '0'){
if (c == '-')tmp = -1;c = getchar();}
while (c >= '0' && c <= '9'){
x = x * 10 + c - '0';c = getchar();}
x *= tmp;
}
const int inf=0x3f3f3f3f;
int mp[55][55],n,k;
bool vis[55];
int pre[55][55];
int dis[55];
int dfs(int k){
memset(dis,inf,sizeof dis);
memset(vis,false,sizeof vis);
dis[1]=0;vis[1]=1;
int cur=1;
for(int i=1;i<n;++i){
int tmp=-1,minn=inf;
for(int j=1;j<=n;++j){
if(vis[j]) continue;
if(dis[j]>dis[cur]+mp[cur][j]){
dis[j]=dis[cur]+mp[cur][j];
pre[k][j]=cur;
}
if(minn>dis[j]){
minn=dis[j];
tmp=j;
}
}
vis[tmp]=1;
cur=tmp;
}
if(k==0) return dis[n];
int ans=dis[n],x=n;
while(pre[k][x]!=-1){
int tmp=mp[x][pre[k][x]];
mp[x][pre[k][x]]=mp[pre[k][x]][x]=inf;
ans=max(ans,dfs(k-1));
mp[x][pre[k][x]]=mp[pre[k][x]][x]=tmp;
x=pre[k][x];
}
return ans;
}
int main(){
ios::sync_with_stdio(false);cin.tie(nullptr); cout.tie(nullptr);
int T;read(T);
while(T--){
memset(pre,-1,sizeof pre);
memset(mp,inf,sizeof mp);
read(n);read(k);
int m=n*(n-1)/2;
for(int i=1;i<=m;++i){
int u,v,w;read(u),read(v),read(w);
mp[u][v]=mp[v][u]=w;
}
printf("%d\n",dfs(k));
}
return 0;
}
Deliver the Cake–hdu 6805
传送门
思路
- 此题最关键的就是对M的点进行拆点,拆成L,R
- 拆点之后就是比较麻烦的建边操作, L − > L L->L L−>L 和 R − > R R->R R−>R的权值是 w w w,反之 L − > R L->R L−>R和 R − > L R->L R−>L的权值是 w + x w+x w+x
- 对所有点进行建边后,开一个新的源点 s t st st 与汇点 e d ed ed
写的时候越写越像网络流…,如果起点为 L L L,则建边 ( s t − > s L ) = 0 (st->s_L)=0 (st−>sL)=0,如果为 R R R就建边 ( s t − > s R ) = 0 (st->s_R)=0 (st−>sR)=0, M M M就同时建立 ( s t − > s L ) = 0 , ( s t − > s R ) = 0 (st->s_L)=0,(st->s_R)=0 (st−>sL)=0,(st−>sR)=0,同理对汇点 e d ed ed操作同样如此 - 复杂的建边完成之后就是无脑跑一遍 d i j k s t r a dijkstra dijkstra
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
inline void read(T &x)
{
int tmp = 1;char c = getchar();x = 0;
while (c > '9' || c < '0'){
if (c == '-')tmp = -1;c = getchar();}
while (c >= '0' && c <= '9'){
x = x * 10 + c - '0';c = getchar();}
x *= tmp;
}
const int inf=0x3f3f3f3f;
const int mod=998244353;
const int N=1e5+10;
const int M=3e6+10;
int head[N<<1],cnt;
struct edge{
int next,to;
ll w;
}e[M];
void add(int u,int v,ll w){
e[cnt].to=v;
e[cnt].next=head[u];
e[cnt].w=w;
head[u]=cnt++;
}
int st,ed;
struct node{
int v;
ll d;
node(int v,ll d):v(v),d(d){
}
bool friend operator<(const node &a,const node &b){
return a.d>b.d;
}
};
bool vis[N<<1];
ll dijkstra(){
priority_queue<node>que;
que.push(node(st,0));
memset(vis,false,sizeof vis);
while(!que.empty()){
node vn=que.top();
que.pop();
if(vn.v==ed){
return vn.d;
}
if(vis[vn.v]) continue;
vis[vn.v]=1;
int u=vn.v;
for(int i=head[u];~i;i=e[i].next){
int v=e[i].to;
if(vis[v]) continue;
que.push(node(v,vn.d+e[i].w));
}
}
}
int n,m,s,t;ll x;
char s0[N];
int main(){
ios::sync_with_stdio(false);cin.tie(nullptr); cout.tie(nullptr);
int T;read(T);
while(T--){
memset(head,-1,sizeof head);cnt=0;
read(n),read(m),read(s),read(t),read(x);
scanf("%s",s0+1);
for(int i=1;i<=m;++i){
int u,v;ll w;
read(u),read(v),read(w);
if(s0[u]=='M'&&s0[v]=='M'){
add(u<<1,v<<1,w);add(v<<1,u<<1,w);
add(u<<1,v<<1|1,w+x);add(v<<1|1,u<<1,w+x);
add(u<<1|1,v<<1,w+x);add(v<<1,u<<1|1,w+x);
add(u<<1|1,v<<1|1,w);add(v<<1|1,u<<1|1,w);
}
else if(s0[u]=='L'&&s0[v]=='L'){
add(u<<1,v<<1,w);add(v<<1,u<<1,w);
}
else if(s0[u]=='L'&&s0[v]=='R'){
add(u<<1,v<<1|1,w+x);add(v<<1|1,u<<1,w+x);
}
else if(s0[u]=='L'&&s0[v]=='M'){
add(u<<1,v<<1,w);add(v<<1,u<<1,w);
add(u<<1,v<<1|1,w+x);add(v<<1|1,u<<1,w+x);
}
else if(s0[u]=='M'&&s0[v]=='L'){
add(u<<1,v<<1,w);add(v<<1,u<<1,w);
add(u<<1|1,v<<1,w+x);add(v<<1,u<<1|1,w+x);
}
else if(s0[u]=='M'&&s0[v]=='R'){
add(u<<1,v<<1|1,w+x);add(v<<1|1,u<<1,w+x);
add(u<<1|1,v<<1|1,w);add(v<<1|1,u<<1|1,w);
}
else if(s0[u]=='R'&&s0[v]=='L'){
add(u<<1|1,v<<1,w+x);add(v<<1,u<<1|1,w+x);
}
else if(s0[u]=='R'&&s0[v]=='M'){
add(u<<1|1,v<<1,w+x);add(v<<1,u<<1|1,w+x);
add(u<<1|1,v<<1|1,w);add(v<<1|1,u<<1|1,w);
}
else if(s0[u]=='R'&&s0[v]=='R'){
add(u<<1|1,v<<1|1,w);add(v<<1|1,u<<1|1,w);
}
}
st=0;ed=1;
if(s0[s]=='L') add(st,s<<1,0);
else if(s0[s]=='R') add(st,s<<1|1,0);
else{
add(st,s<<1,0);
add(st,s<<1|1,0);
}
if(s0[t]=='L') add(t<<1,ed,0);
else if(s0[t]=='R') add(t<<1|1,ed,0);
else{
add(t<<1,ed,0);
add(t<<1|1,ed,0);
}
printf("%lld\n",dijkstra());
}
return 0;
}
Tetrahedron–hdu 6814
传送门
思路
-
首先先把关于 1 h 2 \frac{1}{h^2} h21 的相关式子算出来,推理过程如下
-
利用体积公式 V = 1 3 ∗ a ∗ 1 2 ∗ ( b ∗ c ) = S A B C ∗ 1 3 ∗ h V=\frac{1}{3}*a*\frac{1}{2}*(b*c)=S_{ABC}*\frac{1}{3}*h V=31∗a∗21∗(b∗c)=SABC∗31∗h
= > 1 h 2 = 4 S A B C 2 a 2 b 2 c 2 =>\frac{1}{h^2}=\frac{4S^2_{ABC}}{a^2b^2c^2} =>h21=a2b2c24SABC2 其中 a , b , c a,b,c a,b,c为题目图中的那三个边 -
利用海伦公式求出 S A B C 2 = a 2 b 2 + a 2 c 2 + b 2 c 2 4 S^2_{ABC}=\frac{a^2b^2+a^2c^2+b^2c^2}{4} SABC2=4a2b2+a2c2+b2c2
-
将步骤②代入①中,
= > 1 h 2 = a 2 b 2 + a 2 c 2 + b 2 c 2 a 2 b 2 c 2 = 1 a 2 + 1 b 2 + 1 c 2 =>\frac{1}{h^2}=\frac{a^2b^2+a^2c^2+b^2c^2}{a^2b^2c^2}=\frac{1}{a^2}+\frac{1}{b^2}+\frac{1}{c^2} =>h21=a2b2c2a2b2+a2c2+b2c2=a21+b21+c21
-
-
题目要求 ∀ a ∈ [ 1 , n ] , ∀ b ∈ [ 1 , n ] , ∀ c ∈ [ 1 , n ] \forall a\in[1,n],\forall b\in[1,n],\forall c\in[1,n] ∀a∈[1,n],∀b∈[1,n],∀c∈[1,n],就是求
a n s = ∑ i = 1 n 1 i 2 + ∑ j = 1 n 1 j 2 + ∑ k = 1 n 1 k 2 = 3 ∑ i = 1 n 1 i 2 ans=\sum_{i=1}^{n}{\frac{1}{i^2}}+\sum_{j=1}^{n}{\frac{1}{j^2}}+\sum_{k=1}^{n}{\frac{1}{k^2}}=3\sum_{i=1}^{n}{\frac{1}{i^2}} ans=i=1∑ni21+j=1∑nj21+k=1∑nk21=3i=1∑ni21 -
最后输出期望值 a n s n \frac{ans}{n} nans即可
-
推出公式后由于样例 T T T 较大,直接预处理出范围内所有 i n v inv inv,并求出所有范围内得 a n s ans ans即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
inline void read(T &x)
{
int tmp = 1;char c = getchar();x = 0;
while (c > '9' || c < '0'){
if (c == '-')tmp = -1;c = getchar();}
while (c >= '0' && c <= '9'){
x = x * 10 + c - '0';c = getchar();}
x *= tmp;
}
const int inf=0x3f3f3f3f;
const int mod=998244353;
const int N=6e6+10;
ll inv[N],ans[N];
int main(){
ios::sync_with_stdio(false);cin.tie(nullptr); cout.tie(nullptr);
inv[1]=1;
for(int i=2;i<=N-10;++i) inv[i]=mod-(mod/i)*inv[mod%i]%mod;
ans[1]=1;
for(int i=2;i<=N-10;++i) ans[i]=(ans[i-1]+inv[i]*inv[i]%mod)%mod;
int T;read(T);
while(T--){
int n;read(n);
ll res=ans[n]*inv[n]%mod*3%mod;
printf("%lld\n",res);
}
return 0;
}
Paperfolding–hdu 6822
题意
先进行上下折叠或者左右折叠,最后再从中心点横切一刀,竖切一刀,求出切割后的纸片数 n u m ( S ) num(S) num(S),最后求得到这些此片数可能的操作数 c n t ( S ) cnt(S) cnt(S) ,得期望值 E ( n u m ( S ) ) = n u m ( S ) c n t ( S ) E(num(S))=\frac{num(S)}{cnt(S)} E(num(S))=cnt(S)num(S)
思路
- 首先需要求 n u m ( S ) num(S) num(S),先给出公式 n u m ( S ) = ( 2 x + 1 ) ∗ ( 2 y + 1 ) num(S)=(2^x+1)*(2^y+1) num(S)=(2x+1)∗(2y+1) ,其中 x x x 是水平方向的可能操作数, y y y 是竖直方向的总操作数,并满足 x + y = n x+y=n x+y=n. 推理过程如下
- 先讲水平方向上,最后会进行补刀,所以默认 + 1 +1 +1
- 对于每次折叠操作,对最后纸片贡献度为 ∗ 2 *2 ∗2
- 求 E ( n u m ( S ) ) = 2 n ∑ i = 0 n C n i ∗ ( 2 i + 1 ) ∗ ( 2 n − i + 1 ) 4 n E(num(S))=\frac{2^n\sum_{i=0}^{n}C_n^i*(2^i+1)*(2^{n-i}+1)}{4^n} E(num(S))=4n2n∑i=0nCni∗(2i+1)∗(2n−i+1)其中分子中 2 n 2^n 2n是每一次水品操作或竖直操作都有两种可能性;分母 4 n 4^n 4n 为总的可能性
- 化简得 E ( n u m ( S ) ) = 2 n + 1 + ∑ i = 0 n C n i ∗ ( 2 i + 2 n − i ) 2 n E(num(S))=2^n+1+\frac{\sum_{i=0}^{n}C_n^i*(2^i+2^{n-i})}{2^n} E(num(S))=2n+1+2n∑i=0nCni∗(2i+2n−i)这个公式还不算优美,还是会T
- 继续化简,找出规律得到 ∑ i = 0 n C n i ∗ 2 i = 3 n \sum_{i=0}^nC_n^i*2^i=3^n ∑i=0nCni∗2i=3n
- 最终得 E ( n u m ( S ) ) = 2 n + 1 + 3 n 2 n ∗ 2 E(num(S))=2^n+1+\frac{3^n}{2^n}*2 E(num(S))=2n+1+2n3n∗2
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
inline void read(T &x)
{
int tmp = 1;char c = getchar();x = 0;
while (c > '9' || c < '0'){
if (c == '-')tmp = -1;c = getchar();}
while (c >= '0' && c <= '9'){
x = x * 10 + c - '0';c = getchar();}
x *= tmp;
}
const int inf=0x3f3f3f3f;
const int mod=998244353;
const int N=6e6+10;
ll fp(ll x,ll y){
ll ans=1;
while(y){
if(y&1) ans=ans*x%mod;
x=x*x%mod;
y>>=1;
}
return ans;
}
int main(){
int T;read(T);
while(T--){
ll n;read(n);
ll tmp=fp(2,n%(mod-1));
ll ans=(tmp+1+2*fp(3,n%(mod-1))%mod*fp(tmp,mod-2)%mod)%mod;
printf("%lld\n",ans);
}
return 0;
}
Go Running–hdu 6808
传送门
思路
- 对于每一个点 ( t , x ) (t,x) (t,x),可得他两个方向跑的时候斜率分别为 k = 1 , − 1 k=1,-1 k=1,−1,可得到他们相应的在 y y y 轴上的点为 ( x + t ) , ( x − t ) (x+t),(x-t) (x+t),(x−t)
- 所以对于两个方向,使 k = 1 k=1 k=1 的点放一边, k = − 1 k=-1 k=−1 的点放在另一边,正好就是一个二分图,这道题的本意就是求最小点覆盖,对每个点建立 ( k = 1 ) − > ( k = − 1 ) (k=1)->(k=-1) (k=1)−>(k=−1)的边
- 最后可以用二分图的 HK算法或者网络流来跑出答案,下面代码使用 HK算法
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T>
inline void read(T &x)
{
int tmp = 1;char c = getchar();x = 0;
while (c > '9' || c < '0'){
if (c == '-')tmp = -1;c = getchar();}
while (c >= '0' && c <= '9'){
x = x * 10 + c - '0';c = getchar();}
x *= tmp;
}
const int inf=0x3f3f3f3f;
const int mod=998244353;
const int N=2e5+10;
const int M=1e6+10;
int head[N],cnt;
struct edge{
int next,to;
}e[M];
void add(int u,int v){
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt++;
}
int tot;
int matchx[N],matchy[N],dx[N],dy[N];
bool vis[N];
bool bfs(){
memset(dx,0,sizeof dx);
memset(dy,0,sizeof dy);
queue<int>que;
for(int i=1;i<=tot;++i){
if(matchx[i]==-1) que.push(i);
}
bool ok=false;
while(!que.empty()){
int x=que.front();que.pop();
for(int i=head[x];~i;i=e[i].next){
int v=e[i].to;
if(!dy[v]){
dy[v]=dx[x]+1;
if(matchy[v]==-1) ok=true;
else{
dx[matchy[v]]=dy[v]+1;
que.push(matchy[v]);
}
}
}
}
return ok;
}
bool dfs(int x){
for(int i=head[x];~i;i=e[i].next){
int v=e[i].to;
if(dy[v]==dx[x]+1){
dy[v]=0;
if(matchy[v]==-1||dfs(matchy[v])){
matchy[v]=x;
matchx[x]=v;
return true;
}
}
}
return false;
}
int solve(){
memset(matchx,-1,sizeof matchx);
memset(matchy,-1,sizeof matchy);
int ans=0;
while(bfs()){
for(int i=1;i<=tot;++i){
if(matchx[i]==-1&&dfs(i)) ++ans;
}
}
return ans;
}
int main(){
ios::sync_with_stdio(false);cin.tie(nullptr); cout.tie(nullptr);
int T;read(T);
while(T--){
map<int,int>num;
memset(head,-1,sizeof head);tot=cnt=0;
int n;read(n);
for(int i=1;i<=n;++i){
int x,y;read(x),read(y);
if(!num[x+y]) num[x+y]=++tot;
if(!num[x-y]) num[x-y]=++tot;
add(num[x+y],num[x-y]);
}
printf("%d\n",solve());
}
return 0;
}