杭电多校部分习题

Tokitsukaze and Rescue–hdu 6797

传送门
思路

  • 对最多5个边进行删除操作,即将边权值设置成 i n f inf inf即可
  • 对于每一次的删边操作,枚举每一次跑的最短路径中的 n − 1 n-1 n1条边
  • 最后在删了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 的相关式子算出来,推理过程如下

    1. 利用体积公式 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=31a21(bc)=SABC31h
      = > 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为题目图中的那三个边

    2. 利用海伦公式求出 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

    3. 将步骤②代入①中,
      = > 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=1ni21+j=1nj21+k=1nk21=3i=1ni21

  • 最后输出期望值 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 +1
    2. 对于每次折叠操作,对最后纸片贡献度为 ∗ 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))=4n2ni=0nCni(2i+1)(2ni+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+2ni=0nCni(2i+2ni)这个公式还不算优美,还是会T
  • 继续化简,找出规律得到 ∑ i = 0 n C n i ∗ 2 i = 3 n \sum_{i=0}^nC_n^i*2^i=3^n i=0nCni2i=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+2n3n2
#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),(xt)
  • 所以对于两个方向,使 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;
}

猜你喜欢

转载自blog.csdn.net/bloom_er/article/details/107966028
今日推荐