ARC143 小记

A

不妨 \(A<B<C\),判断若 \(A+B<C\) 无解,否则输出 \(C\)

B

题意:给定一个 \(N\times N\) 方形,填入 \(1\)\(N^2\),求每一列的最大值与每一行的最小值不重复的方案数

设最大值与最小值重复的点为坏点,那么假设坏点有至少两个,对于坏点 \(a,b\) ,设它们行列分别交于 \(c,d\) ,那么 \(a<c<b,b<d<a\) 矛盾

所以至多一个坏点,枚举坏点的值,再往坏点所在行和列填数,剩下的随意排列,即可求出不合法方案总和

#include <cstdio>
using namespace std;
int read(){
	char c=getchar();int x=0;
	while(c<48||c>57) c=getchar();
	do x=(x<<1)+(x<<3)+(c^48),c=getchar();
	while(c>=48&&c<=57);
	return x;
}
const int P=998244353;
const int N=250003;
int fac[N],fiv[N],inv[N];
int C(int n,int k){
	if(k<0||k>n) return 0;
	return 1ll*fac[n]*fiv[n-k]%P;
}
int main(){
	int n=read();
	fac[0]=fiv[0]=inv[1]=1;
	for(int i=1;i<=n*n;++i){
		if(i>1) inv[i]=1ll*inv[P%i]*(P-P/i)%P;
		fac[i]=1ll*fac[i-1]*i%P;
		fiv[i]=1ll*fiv[i-1]*inv[i]%P;
	}
	int res=fac[n*n];
	for(int i=1;i<=n*n;++i)
		res=(res+P-1ll*n*n*C(i-1,n-1)%P*C(n*n-i,n-1)%P*fac[n*n-2*n+1]%P)%P;
	printf("%d\n",res);
	return 0;
}

C

题意:有若干堆石子,甲可以选择若干堆每堆取走 \(X\) 个,乙可以选择若干堆每堆取走 \(Y\) 个,甲乙每次操作必须取至少一堆,谁不能取就输,甲先操作,问谁必胜

妙题,考虑每堆石子对 \(X+Y\) 取模余数 \(a_i\),若 \(\forall i,0\leq a_i < X\) ,那么乙一定必胜,因为若甲选择了取某些堆,那么这些堆一定有 \(\forall i,X\leq a_i < X+Y\) ,这是乙再次在这些堆各取一次,直到所有石子数都比 \(X\) 小,甲输

现在假设 \(\exists i,X\leq a_i < X+Y\) ,那么对于每一堆石子,都考虑取/不取能否使 \(0 \leq a_i<Y\) ,如果存在一堆石子无论甲取/不取都无法满足条件,此时一定有 \(X>Y\) ,乙再次合理操作即可把甲压到必败态,此时甲必败

#include <cstdio>
using namespace std;
int read(){
	char c=getchar();int x=0;
	while(c<48||c>57) c=getchar();
	do x=(x<<1)+(x<<3)+(c^48),c=getchar();
	while(c>=48&&c<=57);
	return x;
}
int main(){
	int n=read(),a=read(),b=read();
	if(a>b){
		for(int i=1;i<=n;++i){
			int t=read();
			if(t%(a+b)<b) continue;
			if(t>=a&&(t-a)%(a+b)<b) continue;
			puts("Second");
			return 0;
		}
		puts("First");
		return 0;
	}
	else{
		for(int i=1;i<=n;++i){
			int t=read();
			if(t%(a+b)<a) continue;
			puts("First");
			return 0;
		}
		puts("Second");
		return 0;
	}
	return 0;
}

D

题意:无向图有 \(2N\) 个节点,第 \(i\) 个和第 \(i+N\) 个节点相连,给出若干个 \(a_i,b_i\) ,选择连 \((a_i+N,b_i)\)\((a_i,b_i+N)\) ,问如何选择可以让图的桥最少

注意到对于 \((a_i,b_i)\) 这类的边,如果其成为桥无论怎么选边它都是桥,考虑减少形如 \((i,i+N)\) 这样的桥

那么可以选择造环,在 DFS 树上尽可能地覆盖更多边即可,一遍 DFS 搞定

#include <cstdio>
using namespace std;
int read(){
	char c=getchar();int x=0;
	while(c<48||c>57) c=getchar();
	do x=(x<<1)+(x<<3)+(c^48),c=getchar();
	while(c>=48&&c<=57);
	return x;
}
const int N=200003;
int a[N],b[N];
int n,m;
int hd[N],ver[N<<1],nxt[N<<1],id[N<<1],tot;
bool dir[N<<1],sd[N],ok[N];
void add(int u,int v,int w,bool d){nxt[++tot]=hd[u];hd[u]=tot;ver[tot]=v;id[tot]=w;dir[tot]=d;}
bool vis[N];
int rk[N],num;
void dfs(int u){
	vis[u]=1;rk[u]=++num;
	for(int i=hd[u];i;i=nxt[i]){
		if(!ok[id[i]]) sd[id[i]]=dir[i],ok[id[i]]=1;
		if(!vis[ver[i]]) dfs(ver[i]);
	}
}
int main(){
	n=read();m=read();
	for(int i=1;i<=m;++i) a[i]=read();
	for(int i=1;i<=m;++i) b[i]=read();
	for(int i=1;i<=m;++i){
		add(a[i],b[i],i,0);
		add(b[i],a[i],i,1);
	}
	for(int i=1;i<=n;++i) if(!vis[i]) dfs(i);
	for(int i=1;i<=m;++i) if(sd[i]) putchar('1');else putchar('0');
	putchar('\n');
	return 0;
}

E

题意:一棵树,树上有硬币,初始有正面有反面,一次操作可以选择一个正面朝上的硬币移除,然后翻转树上所有相邻节点上的硬币,问如何操作可以移除所有硬币

考虑每一个硬币被移除时刻 \(d_u\) ,那么对于一个原本正面朝上的硬币,相邻节点一定有偶数个 \(d_i<d_u\) ,反面则有奇数个

对于叶子来说,它与父亲的时间戳大小关系已经确定,从下往上递推既可求出每一个节点和父亲的时间戳大小关系

如果根不满足奇偶性条件判无解,否则对于所有的时间戳大小关系建图,用堆跑拓扑排序,做完

#include <cstdio>
#include <queue>
using namespace std;
int read(){
	char c=getchar();int x=0;
	while(c<48||c>57) c=getchar();
	do x=(x<<1)+(x<<3)+(c^48),c=getchar();
	while(c>=48&&c<=57);
	return x;
}
const int N=200003;
priority_queue< int,vector<int>,greater<int> > q;
int n,rk,o[N];
int hd[N],ver[N<<1],nxt[N<<1],tot;
int deg[N],ft[N],ghd[N],gver[N],gnxt[N],gtot;
bool s[N];
void add(int u,int v){nxt[++tot]=hd[u];hd[u]=tot;ver[tot]=v;}
void gadd(int u,int v){gnxt[++gtot]=ghd[u];ghd[u]=gtot;gver[gtot]=v;}
bool dfs(int u,int fa){
	ft[u]=fa;
	for(int i=hd[u],v;i;i=nxt[i])
		if((v=ver[i])^fa)
			s[u]^=!dfs(v,u);
	return s[u];
}
int main(){
	n=read();
	for(int i=1;i<n;++i){
		int u=read(),v=read();
		add(u,v);add(v,u);
	}
	char c=getchar();
	while(c!='W'&&c!='B') c=getchar();
	for(int i=1;i<=n;++i) s[i]=(c=='B'),c=getchar();
	if(dfs(1,0)){puts("-1");return 0;}
	for(int i=2;i<=n;++i)
		if(s[i]) gadd(ft[i],i),++deg[i];
		else gadd(i,ft[i]),++deg[ft[i]];
	for(int i=1;i<=n;++i) if(!deg[i]) q.push(i);
	while(!q.empty()){
		int u=q.top();q.pop();
		printf("%d ",u);
		for(int i=ghd[u];i;i=gnxt[i])
			if(!--deg[gver[i]]) q.push(gver[i]);
	}
	putchar('\n');
	return 0;
}
posted @ 2022-06-28 22:06  yyyyxh  阅读(96)  评论(0编辑  收藏  举报