AtCoder Grand Contest 017 题解


A - Biscuits

题目:

给出 \(n\) 个物品,每个物品有一个权值。
问有多少种选取方式使得物品权值之和 \(\bmod\space 2\)\(p\).
\(n \leq 50\)

题解:

记录一下 \(n\) 个物品中权值是奇数的数的个数.
分类讨论一下喽...

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
	x=0;static char ch;static bool flag;flag = false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
const int maxn = 128;
int n,p,x,a,b;
int main(){
	read(n);read(p);
	rep(i,1,n){
		read(x);
		if(x & 1) ++a;
		else ++b;
	}
	if(a == 0){
		if(p == 0) printf("%lld\n", 1LL << n);
		else puts("0");
	}else printf("%lld\n", 1LL <<n-1);
	return 0;
}

B - Moderate Differences

题目:

一个长为 \(n\) 的序列,给出左右两端的元素,问能否在中间的 \(n-2\) 个位置之中填入一些元素使得所有相邻元素的差的绝对值在 \([c,d]\) 内。
\(n \leq 500000, a,b,c,d \leq 10^9\)

题解:

我们将所有的填入与前一项差在某一范围内的元素视作将这个元素改变的限制。
我们知道这个元素一定是经过若干次上升和若干次下降后到达右端。
所以我们可以枚举上升多少次,相应的就知道了下降多少次。
这样我们可以计算出来上升的区间范围和下降的区间范围。
计算一下就好了.

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(ll &x){
	x=0;static char ch;static bool flag;flag = false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)     
int main(){
    ll n,a,b,c,d;read(n);read(a);read(b);read(c);read(d);
    if(n == 1){
		if(a != b) puts("NO");
		else puts("YES");
    }else{-- n;
		for(ll i = 0, j;i <= n;++i){
			j = n - i;
			ll x = i*c, y = i*d;
			ll X = j*c + b-a, Y = j*d + b-a;
			if(y < X) continue;
			if(Y < x) continue;
			puts("YES");return 0;
		}puts("NO");
    }
    return 0;
}

C - Snuke and Spells

题目:

在一个长度为 \(i\) 的序列中施展咒语会消掉所有的值为 \(i\) 的项。
带动态修改某一点元素值地查询需要改掉多少数才能使得序列可以通过不断施展咒语而消掉。
\(n,m \leq 200000\)

题解:

我们分别考虑每个元素。
我们发现如果第 \(i\) 个元素有 \(s_i\) 中。
那么当长度到达 \(i\) 时下一步就会到达 \(i - s_i\)
我们用一条 \([i-s_i,i]\) 的线段来表示这么一个数。
容易发现只有当所有 \([0,n]\) 的所有点之间的线段都被覆盖的时候才可行。
那么一次修改的话我们又最多只能多覆盖一条线段。
(最多使\([i-s_i,i] \to [i-s_i-1,i]\))
所以最小的修改此时就是没有被覆盖的线段条数。
记录一下 \(num_i\) 表示覆盖了 \([i-1,i]\) 的线段条数即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
	x=0;static char ch;static bool flag;flag = false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
const int maxn = 200010;
int a[maxn],ws[maxn],num[maxn];
int main(){
	int n,m;read(n);read(m);
	rep(i,1,n) read(a[i]),ws[a[i]] ++ ;
	int ans = 0;
	rep(i,1,n) if(ws[i]){
		num[max(i-ws[i]+1,1)] ++ ;
		num[min(i+1,n+1)] -- ;
	}
	rep(i,1,n) num[i] += num[i-1],ans += (num[i] == 0);
	while(m--){
		int t,y,x;read(t);read(y);
		x = a[t];a[t] = y;
		if((x-ws[x]+1) >= 1 && (x-ws[x]+1) <= n){
			if(-- num[x-ws[x]+1] == 0) ++ ans;
		}-- ws[x];++ ws[y];
		if((y-ws[y]+1) >= 1 && (y-ws[y]+1) <= n){
			if(++ num[y-ws[y]+1] == 1) -- ans;
		}printf("%d\n",ans);
	}
	return 0;
}

D - Game on Tree

题目:

给定一个 \(n\) 个点的树。Alice和Bob在上面玩游戏。
一次操作切断一条边,没有 \(1\) 节点的部分被删掉
无法操作就输. \(n \leq 100000\)

题解:

雅礼集训讲课原题。
考虑子树的合并就好了。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
	x=0;static char ch;static bool flag;flag = false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
const int maxn = 100010;
struct Edge{
	int to,next;
}G[maxn<<1];
int head[maxn],cnt;
void add(int u,int v){
	G[++cnt].to = v;
	G[cnt].next = head[u];
	head[u] = cnt;
}
int fa[maxn],g[maxn];
void dfs(int u){
	g[u] = 0;
	for(rg i = head[u],v;i;i=G[i].next){
		v = G[i].to;if(v == fa[u]) continue;
		fa[v] = u;dfs(v);
		g[u] ^= (g[v] + 1);
	}
}
int main(){
	int n;read(n);
	int u,v;
	rep(i,2,n){
		read(u);read(v);
		add(u,v);add(v,u);
	}dfs(1);
	puts(g[1] == 0 ? "Bob" : "Alice");
	return 0;
}

E - Jigsaw

题目:

叙述不清,自己看题.
\(link\space to\space Atcoder\)

题解:

很容易想到接口什么的。
每个物品实际上都有两个接口。
考虑都没一段物品两端一定都是落地的。
我们考虑让所有左端落地的左端接口参数为正。反之为负。
所有右端落地的右端接口参数为负,反之为正。
我们发现所有右端接口参数等于左端接口参数的两个物品可以拼接。
考虑转化模型:
抽象出 \(2H\) 个点 \(-H,-(H-1), ... , -1 , 1 , 2 , ... ,H\)
那么对于我们物品设左端接口参数为 \(u\) 右端接口参数为 \(v\).
我们就连接一条 \((u,v)\) 的边。
那么现在的问题就是是否能够将所有的边拆分成若干条路径并且边不重复使用。
使得所有的路径都是从正数节点开始,负数节点结束。
对于这个判断,我们直接判断一下:

  • 所有正数节点是否都满足 \(出度 \geq 入度\)
  • 所有负数节点是否都满足 \(入读 \geq 出度\)
  • 所有弱连通分量中至少存在一个满足 \(出度 \neq 入读\) 的点

如果都满足那么可行。
不难发现这样是对的。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
	x=0;static char ch;static bool flag;flag = false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
const int maxn = 512;
const int zero = 250;
int fa[maxn],siz[maxn],oud[maxn],ind[maxn];bool h[maxn];
int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);}
inline void Union(int x,int y){
	x = find(x);y = find(y);
	if(x == y) {++siz[x];return ;}
	fa[x] = y;siz[y] += siz[x] + 1;
}
int main(){
	int n,H;read(n);read(H);
	rep(i,1,H)  fa[i+zero] = i+zero,siz[i+zero] = 1;
	rep(i,-H,-1)fa[i+zero] = i+zero,siz[i+zero] = 1;
	int a,b,c,d;
	rep(i,1,n){
		int x,y;
		read(a);read(b);read(c);read(d);
		if(c == 0) x = a;else x = -c;
		if(d == 0) y = -b;else y = d;
		++ oud[x + zero];++ ind[y + zero];
		Union(x+zero,y+zero);
	}
	rep(i,1,H)  if(oud[i+zero] < ind[i+zero]) return puts("NO"),0;
	rep(i,-H,-1)if(ind[i+zero] < oud[i+zero]) return puts("NO"),0;
	rep(i,1,H)  h[find(i+zero)] |= (ind[i+zero]!=oud[i+zero]);
	rep(i,-H,-1)h[find(i+zero)] |= (ind[i+zero]!=oud[i+zero]);
	rep(i,1,H)  if(siz[find(i+zero)] > 1 && (find(i+zero) == i+zero) && (h[i+zero] == false)) return puts("NO"),0;
	rep(i,-H,-1)if(siz[find(i+zero)] > 1 && (find(i+zero) == i+zero) && (h[i+zero] == false)) return puts("NO"),0;
	puts("YES");
	return 0;
}
posted @ 2017-07-11 07:05  Sky_miner  阅读(749)  评论(0编辑  收藏  举报