其他分享
首页 > 其他分享> > 2022杭电多校第十场7、3、9、4

2022杭电多校第十场7、3、9、4

作者:互联网

1007 Even Tree Split

先考虑最简单的情况,如下图的边\((3,4)\),我们把这条边切掉,最后会留下一部分的点数为2,另一部分的点数依然是偶数。

进一步思考可以知道,对于边\((u,v)\),只要v子树的点数是偶数,这条边就可以切除。

再进一步,统计所有可以切除的边的数量,可以知道,只要从中选择任意多条,都可以切除两个或多个偶数个点的部分。直接套组合数公式即可。

const int N=1e5+5,M=2*N,p=998244353;
typedef long long ll;
int T;
int n;
int e[M],ne[M];
int h[N],idx;
int sz[N];
int cnt;

int qmi(int a,int k,int p){
	int tmp=1;
	while(k){
		if(k&1)tmp=(ll)tmp*a%p;
		k>>=1;
		a=(ll)a*a%p;
	}
	return tmp;
}

void adde(int x,int y){
	e[idx]=y; ne[idx]=h[x]; h[x]=idx++;
}

void dfs(int u,int fa){
	sz[u]=1;
	for(int i=h[u];~i;i=ne[i]){
		int v=e[i];
		if(v==fa)continue;
		dfs(v,u);
		sz[u]+=sz[v];
		if(sz[v]%2==0)cnt++;
	}
}

int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		idx=0; memset(h,-1,sizeof(h));
		int x,y;
		cnt=0;
		for(int i=1;i<n;i++){
			scanf("%d%d",&x,&y);
			adde(x,y); adde(y,x);			
		}
		
		dfs(1,0);
		
		printf("%d\n",(qmi(2,cnt,p)-1+p)%p);
	}
	return 0;
}

1003 Wavy Tree

很容易猜到贪心方案。

按每个下标属于波峰还是波谷分两种情况考虑:1. 奇峰偶谷,2. 偶峰奇谷。

然后第一个点不动,从左往右枚举,每个点贪心地移动最少的步数直到满足条件。

const int N=1e6+5;
const long long INF=0x3f3f3f3f3f3f3f3f;
typedef long long ll;
int T;
int b[N];
int c[N];
int n;

int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++)scanf("%d",&c[i]);
		memcpy(b,c,sizeof(c));
		ll ans=INF,res=0;
		//奇谷偶峰
		for(int i=2;i<=n;i++){
			if((i&1) && b[i]>=b[i-1]){ res+=b[i]-b[i-1]+1; b[i]=b[i-1]-1; }
			else if(!(i&1) && b[i]<=b[i-1]){ res+=b[i-1]-b[i]+1; b[i]=b[i-1]+1;}
		}
		ans=min(ans,res);
		memcpy(b,c,sizeof(c)); 
		res=0;
		//偶谷奇峰
		for(int i=2;i<=n;i++){
			if((i&1) && b[i]<=b[i-1]){ res+=b[i-1]-b[i]+1; b[i]=b[i-1]+1; }
			else if(!(i&1) && b[i]>=b[i-1]){ res+=b[i]-b[i-1]+1; b[i]=b[i-1]-1;}		
		}
		ans=min(ans,res);
		
		printf("%lld\n",ans);
	}
	return 0;
}

1009 Painting Game

手动膜你到了n=7的情况后,感觉应该找一些规律了。

首先想不太可能是在中间随便放,于是我们从左往右考虑。

对于第一手的情况,两人的最优操作如下(A表示Alice,B表示Bob):

因为这样A可以一次封住两个格子,B可以使x位置成为必填(最后谁填都无所谓)

对于一个黑格子之后的两个回合,两人的最优操作:

同样的,A是为了封住尽可能多的格子,B是为了创造出x这个必填位置。

因此,我们可以发现必然会存在长度为7的循环节。

typedef long long ll;
int T;
int n;
char s[8];
int a[6][2]={//长度小于7直接打表
	{1,1},
	{1,1},
	{1,2},
	{2,2},
	{2,3},
	{3,3},
};
int lft[2][7]={//余数部分直接打表
	{0,0,1,1,2,2,3},
	{0,0,1,1,1,2,2},
};

int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		scanf("%s",s+1);
		if(s[1]=='A'){
			if(n<=6)printf("%d\n",a[n-1][0]);
			else{
				int res=1;
				n-=2;//先处理第一手的情况
				res+=n/7*3+lft[0][n%7];//循环节和余数
				printf("%d\n",res);
			}
		}
		else{
			if(n<=6)printf("%d\n",a[n-1][1]);
			else{
				int res=2;
				n-=3;
				res+=n/7*3+lft[1][n%7];
				printf("%d\n",res);
			}
		}
	}
	return 0;
}

1004 Average Replacement

动手模拟、猜结论、并查集连通块。

找规律可以直接发现的:

  1. 同一连通块中的点最后值相同。
  2. 最后答案和点的度数有一定关联。

加上一点灵机一动,猜到:

\[每个连通块内的\sum a[i]*(deg[i]+1)是定值 \]

所以最终收敛的数就是用上面那个定值除以\((deg[i]+1)\)。

const int N=1e5+5;
typedef long long ll;
int T;
int n,m;
int w[N];
int f[N],d[N];
ll up[N],dn[N];

int findx(int x){
	if(f[x]!=x)return f[x]=findx(f[x]);
	else return x;
}

int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)scanf("%d",&w[i]);
		for(int i=1;i<=n;i++){
			f[i]=i,d[i]=0;
			up[i]=dn[i]=0;
		}
		
		int x,y;
		for(int i=1;i<=m;i++){
			scanf("%d%d",&x,&y);
			d[x]++,d[y]++;
			int fx=findx(x), fy=findx(y);
			if(fx!=fy)f[fx]=fy;
		}
		
		for(int i=1;i<=n;i++){
			int fx=findx(i);
			up[fx]+=(ll)w[i]*(d[i]+1);
			dn[fx]+=(d[i]+1);
		}
		
		for(int i=1;i<=n;i++)printf("%lf\n",1.0*up[findx(i)]/dn[findx(i)]);
	}
	
	return 0;
}

标签:杭电多校,idx,第十,int,scanf,long,while,2022,ll
来源: https://www.cnblogs.com/tshaaa/p/16610069.html