【CEOI 1999】奇偶性游戏

一道特别玄学的题

Description
你偶尔和朋友玩如下的游戏。你的朋友写下一个由01组成的序列。你选择连续的一段子序列(例如,从第3到第5个数的子序列),问他这一段中1的个数是偶数还是奇数。你的朋友会回答你的问题,然后你可以问他另外一段子序列,等等。你的任务是猜出整个序列。 
你怀疑你朋友的一些回答可能是错误的,并且你希望证明他说了假话。因此你决定写一个程序来帮助你。这个程序将会收到你的一系列问题以及你朋友给出的答案。程序的目标是找到第一个被证明是错误的回答,即,存在一个序列能满足之前所有回答,但加上这个回答后就不存在相应的序列。


Input
第一行有一个整数,代表01序列的长度。长度不超过1000000000。 
第二行有一个正整数,代表询问和回答的数量。数量不超过5000. 
接下来的若干行描述了所有的了询问和回答。每一行包含了一个询问和对此询问的回答:两个整数(所选择的子序列的起止点),一个单词“even”或“odd”(答案,即该子序列中1的个数的奇偶性),其中“even”代表有偶数个,“odd”代表有奇数个。


Output
输出一个整数X。它意味着:存在一个01序列满足前X个奇偶性询问,但不存在满足前X+1个奇偶性询问的序列。如果存在满足所有询问的序列,X就应该是询问的总数。


Sample Input
输入样例1:
10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd 
输入样例2:
10
5
1 2 even
1 4 even
2 4 odd
1 10 even
3 10 even
1 2 even
Sample Output
输出样例1:
3
输出样例2:
5


首先离散化一下(把没有出现过的点全看做偶数,于是可以直接X掉)

然后连边

对于每个询问Q(a,b,c)(c为even或odd)

在a,b间连一条权值为c的边(even==0,odd==1)

但这时发现,如果1,2为even,3,4为even,那么1,4就已经知道是even了

然而程序里得不到体现,

于是将“在a,b间连一条权值为c的边”改成“在a-1,b间连一条权值为c的边

然后惊喜的发现0,4联通了!!!

需要注意的是,如果图上有环,就说明朋友的话可能会起冲突了,

打表证明:如果环上边权值和为even,则不起冲突,反之起冲突

用并查集维护两点是否联通,如果两点联通时要连边,则取消,判断这两点之间的路径权值之和

与这条要加的边权值相加再判断即可

这样保证了图是一棵树,因为数据水,判断环上权值和可以直接暴力!!!

水过!!!!!!

#include<cstdio>
#include<algorithm>
#include<map>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=10002,maxm=10002;
int fir[maxn],dis[maxn],nxt[maxn],id=1,w[maxn];
#define il inline
#define vd void
#define t (dis[i])
il int gi(int x=0){
	char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x;
}
il vd add(int a,int b,int c){
	nxt[++id]=fir[a],fir[a]=id,dis[id]=b,w[id]=c;
}
int Qa[5111],Qb[5111],Qc[5111],nums[11111],k=0;
char str[10];
map<int,int>m;
int fa[maxn];
il int hd(int now){
	if(fa[now]^now)fa[now]=hd(fa[now]);
	return fa[now];
}
il int dfs(int now,int final,int fa,int num){
	if(now==final)return num&1;
	for(int i=fir[now];i;i=nxt[i])
		if(t!=fa){
			int kk=dfs(t,final,now,num^w[i]);
			if(kk!=-1)return kk;
		}
	return -1;
}
int main(){
	int n=gi(),q=gi();
	for(int i=1;i<=q;i++){
		Qa[i]=gi()-1,Qb[i]=gi(),scanf("%s",str);
		if(str[0]=='o')Qc[i]=1;
		else Qc[i]=0;
		nums[++k]=Qa[i],nums[++k]=Qb[i];
	}
	sort(nums+1,nums+q+q+1);
	int s=0;
	for(int i=1;i<=q<<1;i++)if(nums[i]!=nums[i-1])m[nums[i]]=++s;
	for(int i=1;i<=s;i++)fa[i]=i;
	for(int hehehe=1;hehehe<=q;hehehe++){
		int a=m[Qa[hehehe]],b=m[Qb[hehehe]],c=Qc[hehehe];
		if(hd(a)!=hd(b))fa[hd(a)]=hd(b),add(a,b,c),add(b,a,c);
		else if(dfs(a,b,-1,0)!=c){printf("%d\n",hehehe-1);return 0;}
	}printf("%d\n",q);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值