Atcoder AGC016 简要题解

传送门

+/- Rectangle

对于 ( x , y ) (x,y) (x,y),若 x   m o d   h ̸ = 0 x \bmod h \not = 0 xmodh̸=0或者 y   m o d   w ̸ = 0 y \bmod w \not = 0 ymodw̸=0那么填 v v v,否则填 h ∗ w ∗ v − 1 h*w*v-1 hwv1,这样列出等式后把 v v v选一个最大的就行了, 1 0 9 10^9 109的范围够了。

#include <bits/stdc++.h>
using namespace std;
 
const double eps=1e-7;
int H,h,W,w;
inline int val(int i,int j,int a,int b) {return ((i%h)||(j%w)) ? a : b;}
int main() {
	cin>>H>>W>>h>>w;
	if((!(H%h)) && (!(W%w))) return puts("No"),0;
	int v=(1e9-1)/(h*w-1);
	if((long long)v*(H*W-(H/h)*h*(W/w)*w)<=((H/h)*(W/w))) return puts("No"),0;
	puts("Yes");
	for(int i=1;i<=H;i++,cout<<'\n')
		for(int j=1;j<=W;cout<<val(i,j,v,-v*(h*w-1)-1)<<' ',j++);
}

XOR Replace

设异或和为 x x x并放在 a n + 1 a_{n+1} an+1,发现操作就是交换 a i , a n + 1 a_i,a_{n+1} ai,an+1,如果这样做之后序列和 b b b不一样那么肯定无解。否则对于那些不相等的地方,找出一个置换,方案数为置换大小+1(因为第一步要和 n + 1 n+1 n+1先交换),然后发现如果两个置换某个数相等那么两个置换可以合并,于是用个并查集就行了。

#include <bits/stdc++.h>
using namespace std;
typedef pair <int,int> pii;
const int RLEN=1<<18|1;
inline char nc() {
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
	char ch=nc(); int i=0,f=1;
	while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
	while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
	return i*f;
}
 
const int N=1e5+50;
int n,a[N],b[N],step;
int anc[N],vis[N];
map <int,int> s;
set <int> pos[N];
inline void lsh(int *c) {
	vector <int> vec;
	for(int i=1;i<=n+1;i++) vec.push_back(c[i]);
	sort(vec.begin(),vec.end());
	vec.erase(unique(vec.begin(),vec.end()),vec.end());
	for(int i=1;i<=n+1;i++) c[i]=lower_bound(vec.begin(),vec.end(),c[i])-vec.begin()+1; 
} 
inline int ga(int x) {return (anc[x]==x) ? x : (anc[x]=ga(anc[x]));}
inline void merge(int x,int y) {anc[ga(x)]=ga(y);}
int main() {
	n=rd();
	for(int i=1;i<=n;i++) a[i]=rd(), a[n+1]^=a[i], ++s[a[i]]; ++s[a[n+1]];
	for(int i=1;i<=n;i++) b[i]=rd(), b[n+1]^=b[i], --s[b[i]]; --s[b[n+1]];
	for(auto v:s) if(v.second) return puts("-1"),0;
	lsh(a); lsh(b); int m=*max_element(a+1,a+n+2);
	for(int i=1;i<=n+1;i++) 
		if((a[i]^b[i]) || i>n) pos[a[i]].insert(i);
	for(int i=1;i<=m;i++) anc[i]=i;
	for(int i=n+1;i;i--) if(i>n || (a[i]^b[i])){
		if(vis[i]) continue;
		vector <int> vec; 
		for(int j=b[i];;) {
			int nxt=*pos[j].begin();
			pos[j].erase(pos[j].begin());
			vec.push_back(nxt);
			if(nxt==i) break;
			else j=b[nxt];
		}
		for(auto j:vec) merge(b[j],b[i]), vis[j]=1; 
	}
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n+1;i++) if(a[i]^b[i] || i==n+1) {
		if(i<=n) ++step;
		if(!vis[ga(a[i])] && (ga(a[i])==a[i])) vis[ga(a[i])]=1, ++step;
	}
	cout<<step-1<<'\n';
}

Poor Turkeys

傻逼题,不断维护 ( x , y ) (x,y) (x,y)表示不能同时存在的对就可以了。

#include <bits/stdc++.h>
using namespace std;
 
const int RLEN=1<<18|1;
inline char nc() {
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
	char ch=nc(); int i=0,f=1;
	while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
	while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
	return i*f;
}
 
const int N=4e2+5;
 
int n,m;
bitset <N> sta[N],ban;
int main() {
	n=rd(), m=rd();
	for(int i=1;i<=m;i++) {
		int x=rd(), y=rd();
		sta[x]|=sta[y];
		sta[y]|=sta[x];
		for(int j=1;j<=n;j++) {
			int tag1=sta[j].test(x), tag2=sta[j].test(y);
			if(tag1) sta[j].set(y);
			if(tag2) sta[j].set(x);
		}
		sta[x].set(y); sta[y].set(x);
	}
	for(int i=1;i<=n;i++) 
		if(sta[i][i]) ban[i]=1;
	int cnt=0;
	for(int i=1;i<=n;i++) if(!ban[i]) {
		bitset <N> tp=sta[i];
		for(int j=1;j<=n;j++) tp.flip(j);
		cnt+=tp.count();
		tp&=ban;
		cnt-=tp.count();
	} cout<<(cnt-(n-ban.count()))/2<<'\n';
}

Games on DAG

感觉这场最难的是D题。

一个方案先手必胜,一定时 s g 1 ̸ = s g 2 sg_1 \not = sg_2 sg1̸=sg2,于是从小到大依次确定 s g sg sg值,保证转移的时候 s g sg sg大的到小的一定有连边,小的到大的随便连就行了。

#include <bits/stdc++.h>
using namespace std;
 
const int RLEN=1<<18|1;
inline char nc() {
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
	char ch=nc(); int i=0,f=1;
	while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
	while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
	return i*f;
}
 
const int N=17, mod=1e9+7;
inline int add(int x,int y) {return (x+y>=mod) ? (x+y-mod) : (x+y);}
inline int mul(int x,int y) {return (long long)x*y%mod;}
inline int pct(int x) {return __builtin_popcount(x);}
int n,m,g[N],bin[N],f[1<<N];
inline int valid(int s1,int al) {
	if((s1&1) && (s1&2)) return 0;
	int s2=(bin[n]-1)^al, sum=1;
	for(int j=0;j<n;j++) if(s2&bin[j]) {
		if(!(s1&g[j])) return 0;
		int c=pct(s1&g[j]);
		sum=mul(sum,bin[c]-1);
	} 
	for(int j=0;j<n;j++) if(s1&bin[j]) {
		int c=pct(s2&g[j]);
		sum=mul(sum,bin[c]);
	}
	return sum;
}
int main() {
	n=rd(), m=rd();
	for(int i=0;i<=n;i++) bin[i]=1<<i;
	for(int i=1;i<=m;i++) {
		int x=rd()-1, y=rd()-1;
		g[x]|=bin[y];
	}
	f[0]=1;
	for(int s=1;s<bin[n];++s) {
		for(int s2=s;s2;s2=(s2-1)&s) {
			int t=valid(s2,s);
			if(t) f[s]=add(f[s],mul(f[s^s2],t));
		}
	}
	cout<<f[bin[n]-1]<<'\n';
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值