zoukankan      html  css  js  c++  java
  • CF1439 E. Cheat and Win

    在一个坐标系上,定义有效的点((x,y))为满足(x and y=0)的点。四相邻的点互相连边。

    可以证明这是棵树,规定((0,0))为根节点。

    一开始有些点是黑点,有些点是白点。两人博弈,每次可以操作一个黑点,将它和它到祖先路径上的任意点集的颜色反转。最后不能操作的输。

    树的初始状态由给出(m)条路径,将(m)条路径的并染黑决定。

    后手想要作弊,他在博弈前做若干次操作,每次选择一个点,将其到根路径上所有点颜色反转。最小化操作次数使得后手赢。

    (mle 10^5)

    (x,yin[0,10^9])


    这是棵树的证明:可以发现((x,y))((x-1,y),(x,y-1))中恰好一个点连边。

    核心问题:博弈必胜的判定条件。

    放结论:按照深度分层,统计每层的黑点个数,如果存在一层的黑点个数为奇数,那么先手必胜。

    证明考虑,当满足这个条件的时候,先手可以用一次操作将每一层的黑点个数调成偶数。到了后手时,他无论怎么操作,操作之后肯定存在一层的黑点个数为奇数。又因为必败状态每层黑点个数为(0),所以一直做下去一定是后手必输。不满足这个条件的证明类似。

    另外有个阴间的考虑方法:可以将原问题拆成若干个子问题,求形如(f_x)表示只有(x)染黑的(sg)值。

    假设这样考虑正确,那么原问题(sg)( xor_{xin Black} f_x)。转移可以将相同深度的点看做是等价的状态,枚举点集转移过去,得到它们的异或和,再将每种转移的情况(mex)起来。归纳一下可以发现(f_x=2^{dep_x})。最终和上面的那个结论是等价的。

    接下来口胡一下为什么这样考虑是正确的。这里会用到我的一些乱搞的理论,缺乏理性证明。

    现在换个问题:每次选择一个不为空的点集,将其反转。区别在于没有限制最下面那个点是黑点。如果最下面那个点是白点,操作过后后手可以重新调整会当前的局面,所以操作白点不会变得更优。

    不过似乎这样调整过后黑点和白点也没什么区别;所以引入一个势函数:(phi(S)=xor_{xin Black(S)} 2^{dep_x})。显然终止时(phi(S)=0),并且它同时也是最小值。因为双方都希望获胜,不希望对方跳到比当前局面优的情况,感性理解得操作的过程中势函数一直单调不增。

    这样操作黑点和白点的区别在于:操作黑点可以使势函数减少。

    于是从这个角度不严谨地说明了两个问题是等价的。

    后面的东西忽略上面乱讲的也没有关系。

    现在改变了问题,于是不同的操作互不影响。

    唯一要考虑的是原问题到了终止态的时候,如何和若干个子问题对应起对应关系呢?

    原问题到终止态的时(sg=0),此时若干个子问题可能还没有结束,尽管如此它们异或和也为(0)。这说明了:如果继续做下去,那么先手必败。这就相当于是终止态了。

    知道了这个结论之后,建个虚树然后随便搞一下即可。

    (LCA)可以(O(lg))求,比较(dfn)序也可以通过求(LCA)做。

    具体就是如果不在同一条线段上,往父亲的方向跳一大步直到到了拐点为止。


    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <map>
    #include <cassert>
    #define N 400005
    #define lowbit(x) ((x)&-(x))
    #define mp(x,y) (make_pair(x,y))
    int n,m;
    struct DOT{
    	int x,y;
    	int dep(){return x+y;}
    	DOT fa(){
    		if (x==0 && y==0) return (DOT){0,-1};
    		if (x==0) return (DOT){x,y-1};
    		if (y==0) return (DOT){x-1,y};
    		return lowbit(x)<lowbit(y)?(DOT){x-1,y}:(DOT){x,y-1};
    	}
    	DOT top(){
    		if (x==0 || y==0) return (DOT){0,0};
    		if (lowbit(x)>lowbit(y)) return (DOT){x,y-(y&lowbit(x)-1)};
    		return (DOT){x-(x&lowbit(y)-1),y};
    	}
    } d[N];
    bool operator<(DOT a,DOT b){return a.x<b.x || a.x==b.x && a.y<b.y;}
    map<DOT,int> id;
    bool operator==(DOT a,DOT b){return a.x==b.x && a.y==b.y;}
    bool same_line(DOT a,DOT b){return a.top()==b.top() && (a.x==b.x || a.y==b.y);}
    DOT LCA(DOT a,DOT b){
    	while (!same_line(a,b))
    		if (a.top().dep()>b.top().dep())
    			a=a.top();
    		else
    			b=b.top();
    	return a.dep()<b.dep()?a:b;
    }
    bool cmpd(DOT a,DOT b){
    	DOT A=a,B=b;
    	while (!same_line(a,b))
    		if (a.top().dep()>b.top().dep())
    			a=a.top();
    		else
    			b=b.top();
    	DOT lca=a.dep()<b.dep()?a:b;
    	if (B==lca) return 0;
    	if (A==lca) return 1;
    	return a.y==b.y?a.x<b.x:a.y>b.y;
    }
    struct edge{DOT u,v;} ed[N];
    struct EDGE{
    	int to;
    	EDGE *las;
    } e[N];
    int ne;
    EDGE *last[N];
    void link(int u,int v){
    	e[ne]={v,last[u]};
    	last[u]=e+ne++;
    }
    int build(){
    	static int st[N];
    	int tp=1;
    	st[1]=1;
    	int _m=m;
    	for (int i=2;i<=m;++i){
    		DOT _lca=LCA(d[i],d[st[tp]]);
    		if (!id[_lca]){
    			d[++_m]=_lca;
    			id[_lca]=_m;
    		}
    		int lca=id[_lca];
    		while (tp>1 && d[lca].dep()<d[st[tp-1]].dep()){
    			link(st[tp-1],st[tp]);
    			--tp;
    		}
    		if (tp && d[lca].dep()<d[st[tp]].dep()){
    			link(lca,st[tp]);
    			--tp;
    			if (!(lca==st[tp]))
    				st[++tp]=lca;
    		}
    		st[++tp]=i;
    	}
    	while (tp>1){
    		link(st[tp-1],st[tp]);
    		--tp;
    	}
    	m=_m;
    	return st[1];
    }
    int c[N],b[N];
    int q[N*2],nq;
    int dep[N];
    int ans[N];
    void dfs(int x,int fa=0){
    	dep[x]=d[x].dep();
    	for (EDGE *ei=last[x];ei;ei=ei->las)
    		dfs(ei->to,x),c[x]+=c[ei->to];
    	if (c[x])
    		q[++nq]=dep[fa]+1,q[++nq]=dep[x]+1;
    	else if (b[x])
    		q[++nq]=dep[x],q[++nq]=dep[x]+1;
    }
    int main(){
    	scanf("%d",&n);
    	for (int i=1;i<=n;++i){
    		int x1,y1,x2,y2;
    		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    		d[++m]={x1,y1};
    		d[++m]={x2,y2};
    		ed[i]={d[m-1],d[m]};
    	}
    	sort(d+1,d+m+1,cmpd);
    	m=unique(d+1,d+m+1)-d-1;
    	for (int i=1;i<=m;++i)
    		id[d[i]]=i;
    	int rt=build();
    	for (int i=1;i<=n;++i){
    		int u=id[ed[i].u],v=id[ed[i].v];
    		int lca=id[LCA(d[u],d[v])];
    		c[u]++,c[v]++,c[lca]-=2;
    		b[lca]=1;
    	}
    	dfs(rt);
    	sort(q+1,q+nq+1);
    	int cnt=0,s=0,last[2]={-2,-2};
    	for (int i=1;i<=nq;++i){
    		s^=1;
    		if (i==nq || q[i]!=q[i+1]){
    			if (last[s]<=last[s^1])
    				cnt++;
    			last[s]=q[i];
    		}
    	}
    	if (last[0]>last[1])
    		cnt--;
    	printf("%d
    ",cnt);
    	return 0;
    }
    
  • 相关阅读:
    nyist 541最强DE 战斗力
    nyist 231 Apple Tree
    nyist 543 遥 控 器
    nyist 233 Sort it
    nyist 517 最小公倍数
    hdu 1sting
    nyist A+B Problem IV
    nyist 522 Interval
    nyist 117 求逆序数
    nyist 600 花儿朵朵
  • 原文地址:https://www.cnblogs.com/jz-597/p/14002710.html
Copyright © 2011-2022 走看看