AtCoder Grand Contest 016

链接

C. +/- Rectangle

构造题。显然我们需要负数越少越好,这样我们每 (w,h) 放一个负数,令他等于 k(wh1)1,其余点等于 k。可以发现这样每 (w,h) 都是符合条件的。

但是这样总和取决于 W%w,H%h 的值。可以发现只要这两个值不是 0,随便取 k=2333 都一定可以。

那么对于 w|W,h|H 的情况,由于每个负数最多贡献 wh 次,所以最后结果一定为负,即不合法。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
	int n,m,w,h;
	scanf("%d%d%d%d",&n,&m,&w,&h);
	if(n%w==0 && m%h==0){puts("No");return 0;}
	puts("Yes"); 
	int b=2333,p=-b*(w*h-1)-1;
	for(int i=1;i<=n;i++,puts(""))
			for(int j=1;j<=m;j++) printf("%d ",i%w==0 && j%h==0?p:b);
	return 0;
}

D. XOR Replace

考虑我们新建的一个权值为所有异或值的点,那么一次操作本质上就是把这个特殊点和某个点的权值交换。

首先如果序列不同显然无解,否则考虑:如果一个点从 ai 要到 bi,那么必然有一个时刻特殊点权值为 bi 然后和它交换。

不妨连一条 biai 的有向边,一开始有一个标记在特殊点权值上,每次可以移动标记到相邻点,对应依次交换。目标是把所有边都走过。

由于这张图每个点一定有入度等于出度,故对于每个联通块需要走恰好边数次,不同联通块之间需要恰好 1 次。

所以最后答案就是边数+联通块数-特殊点是否是孤立点。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
using namespace std;
int a[N],b[N],pa[N],pb[N],f[N];
int find(int x){return x==f[x]?f[x]:(f[x]=find(f[x]));}
bool vis[N];
int main()
{
	int n,v=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),v^=a[i];a[n+1]=v;v=0;
	for(int i=1;i<=n;i++) scanf("%d",&b[i]),v^=b[i];b[n+1]=v;++n;
	for(int i=1;i<=n;i++) pa[i]=a[i],pb[i]=b[i];
	sort(pa+1,pa+n+1);sort(pb+1,pb+n+1);
	for(int i=1;i<=n;i++) if(pa[i]!=pb[i]){puts("-1");return 0;}
	int m=unique(pa+1,pa+n+1)-pa-1;
	for(int i=1;i<=n;i++) a[i]=lower_bound(pa+1,pa+m+1,a[i])-pa;
	for(int i=1;i<=n;i++) b[i]=lower_bound(pa+1,pa+m+1,b[i])-pa;
	int ans=0;
	for(int i=1;i<=n;i++) if(a[i]!=b[i] || i==n) f[a[i]]=a[i],f[b[i]]=b[i],ans+=i<n;
	for(int i=1;i<=n;i++)
		if(a[i]!=b[i]) f[find(a[i])]=find(b[i]);
	for(int i=1;i<=m;i++) ans+=f[i]==i;
	printf("%d\n",max(ans-1,0));
	return 0;
}

E. Poor Turkeys

思维好题。

首先考虑如果需要某个鸡活下来,那么需要那些鸡去替他被吃。

具体来说,我们从后往前处理。假设我们想要 i 活下来,而 ji 必须吃一个,那么 j 就必须替 i 被吃。换句话,在此之前 j 必须活着。

那么对于某一时刻 jk 必须吃一个,而 j 要活到后面才能使 i 活,那么 k 也必须在这里死,在此之前必须或者。

如果 jk 都必须活,那么不好意思 i 必须得死了。

可以发现这样一只鸡最多只能抗一次,所以两只鸡同时活着的充要条件是替他们被吃的鸡没有交集。

直接暴力处理即可。复杂度 O(nm+n3ω)

#include<iostream>
#include<cstdio>
#include<bitset>
#define N 410
#define M 100010
using namespace std;
bitset<N>d[N];//d_i[j] : to save i, if j have to be die
int x[M],y[M];
bool ban[N];
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]);
	for(int i=1;i<=n;i++)
	{
		d[i].set(i);
		for(int j=m;!ban[i] && j>=1;j--)
			if(d[i].test(x[j]) && d[i].test(y[j])) ban[i]=true;
			else if(d[i].test(x[j])) d[i].set(y[j]);
			else if(d[i].test(y[j])) d[i].set(x[j]);
	}
	int res=0;
	for(int i=1;i<=n;i++) if(!ban[i])
			for(int j=i+1;j<=n;j++) if(!ban[j] && !(d[i]&d[j]).any())
			{
//				printf("%d & %d\n",i,j);
//				for(int k=1;k<=n;k++) printf("%d ",d[i].test(k));puts("");
//				for(int k=1;k<=n;k++) printf("%d ",d[j].test(k));puts("");
				res++;
			}
	printf("%d\n",res);
	return 0;
}

F. Games on DAG

神仙题。

首先观察到 n 很小,往 2n,3n 方向考虑。

考虑如果边固定时怎么做。可以直接计算每个点的 SG 函数。具体来说每个点的 SG 值是其连出点 SGmex。最后要求 SG(1)SG(2)

主要到最后的条件。那么我们不妨算出 SG(1)=SG(2) 的方案再用 2m 去减。

考虑 mex 的性质。我们不妨从小往大做,状压已经求得 SG 的点。我们并不需要具体知道其中 SG 的具体值。

fS 表示 只考虑S 集合,SG(1)=SG(2) 的方案数,考虑如何转移。

我们枚举 S 一个子集 T,钦定 T 中的元素 SG(x)>0,非 T 的元素 SG(x)=0。这样 T 中的每个元素都需要非 T 元素连至少一条边,而非 T 集合可以向 T 集合连边,也可以不连,但是不能向非 T 集合连边。

考虑 1,2 显然需要同时属于 T 集合或是同时不属于 T 集合。如果属于 T 集合那么 fSfT,因为可以发现将 T 集合的 SG 整体加 1 就可以得到 S 集合的 SG

分类讨论一下即可。预处理一个点与某个点集的边数,总复杂度 O(m3n)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 16
#define M 210
#define mod 1000000007
using namespace std;
int rd[N][1<<N],_2[M];//the road from u to set {S}
int f[1<<N];
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=_2[0]=1;i<=m;i++) _2[i]=2ll*_2[i-1]%mod;
	int S=1<<n;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);--u,--v;
		rd[u][1<<v]++;
	}
	for(int i=0;i<n;i++)
		for(int s=1;s<S;s++) rd[i][s]=rd[i][s-(s&(-s))]+rd[i][s&(-s)];
	for(int s=3;s<S;s++)
	if(s%4==3)
	{
		f[s]=1;
		for(int t=s&(s-1);t;t=(t-1)&s)//the set except 0
		if(t%4==3)//both s,t \geq 0
		{
			int res=1;
			for(int i=0;i<n;i++) if((s>>i)&1)
			{
				if((t>>i)&1) res=1ll*res*(_2[rd[i][s^t]]-1)%mod;
				else res=1ll*res*_2[rd[i][t]]%mod;
			}
			f[s]=(f[s]+1ll*res*f[t])%mod;
		}
		else if(t%4==0)//both s,t = 0
		{
			int res=1;
			for(int i=0;i<n;i++) if((s>>i)&1)
			{
				if((t>>i)&1) res=1ll*res*(_2[rd[i][s^t]]-1)%mod*_2[rd[i][t]]%mod;
				else res=1ll*res*_2[rd[i][t]]%mod;
			}
			f[s]=(f[s]+res)%mod;
		}
	}
	printf("%d",(_2[m]-f[S-1]+mod)%mod);
	return 0;
}
posted @   Flying2018  阅读(12)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示