zoukankan      html  css  js  c++  java
  • CF1444E. Finding the Vertex

    题目描述

    题解

    设每条边都有一个初始为0的边权,每次查询断成两个块后就把两个块的边权+1,最后得到的树上任意两条边权相同的边之间都有一条边权小于其的边,则操作次数为最小边+1

    把边权反过来,即初始为k每次把两侧-1,则变为相同的之间有大于其的

    考虑dp,设f[i]表示以i为根的子树中能连上来的边的集合,用一个n位二进制数存,则显然是让f越小越好

    加上x到儿子y的权值为i的边之后,f[y]的第i位首先要为0,加上后变成1同时0~i-1位变成0,因为已经有大于其的边了所以失去限制

    因为边权必须要有,所以问题变成了已知a1,a2,...an,求b1,b2,...bn满足bi>ai且b1&b2&...&bn=0,使b1|b2|...|bn最小

    把a+1后按位确定,如果当前位有1则可以把一个最高位为当前位且为1的a消掉该位丢回去,如果不存在或者消成0了则可以完全消掉,如果有>1个则无解

    一共要做n^2次,所以时间复杂度O(n^3log),最后询问当前块最大边即可

    code

    #include <bits/stdc++.h>
    #define fo(a,b,c) for (a=b; a<=c; a++)
    #define fd(a,b,c) for (a=b; a>=c; a--)
    #define ll long long
    //#define file
    using namespace std;
    
    struct type{ll s1,s2;int id;} b[101],s,Inf;
    int a[202][3],ls[101],fa[101],id[101],D[101],f[202],F[202],d[202],n,i,j,k,l,len,x,y,z,mx,mx2,t;
    bool bz[101];
    ll p[51];
    bool operator < (type a,type b) {return !(a.s1>b.s1 || a.s1==b.s1 && a.s2>b.s2);}
    priority_queue<type> hp;
    ifstream f1;
    
    void New(int x,int y) {++len;a[len][0]=y;a[len][1]=ls[x];ls[x]=len;}
    type Xor(type s,int t) {return (t<50)?type{s.s1,s.s2^p[t],s.id}:type{s.s1^p[t-50],s.s2,s.id};}
    bool Zero(type s) {return !s.s1 && !s.s2;}
    type Add(type a) {++a.s2; if (a.s2>=p[50]) a.s2-=p[50],++a.s1;return a;}
    bool Get(type s,int t) {return (t<50)?((s.s2&p[t])>0):((s.s1&p[t-50])>0);}
    bool pd(type a)
    {
    	int i,j,k,l;
    	t=0;
    	fd(i,n-1,0)
    	if (Get(a,i))
    	{
    		if (hp.empty()) {fo(i,1,t) f[d[i]]=F[d[i]];return 1;}
    		s=hp.top(),hp.pop();
    		if (Get(s,i+1)) return 0;
    		if (Get(s,i))
    		{
    			if (!Zero(Xor(s,i))) hp.push(Xor(s,i));
    			else F[s.id]=i,d[++t]=s.id;
    		}
    		else F[s.id]=i,d[++t]=s.id;
    	}
    	else
    	if (!hp.empty())
    	{
    		s=hp.top();
    		if (Get(s,i)) return 0;
    	}
    	
    	if (hp.empty()) {fo(i,1,t) f[d[i]]=F[d[i]];}
    	return hp.empty();
    }
    void dfs(int Fa,int t)
    {
    	int i,j,k,l;
    	fa[t]=Fa;
    	if (a[ls[t]][0]==Fa && D[t]==1) {b[t].s1=b[t].s2=0,b[t].id=t;return;}
    	
    	for (i=ls[t]; i; i=a[i][1])
    	if (a[i][0]!=Fa)
    	id[a[i][0]]=i,dfs(t,a[i][0]);
    	
    	b[t]=Inf,b[t].id=t;
    	fd(i,n-1,0)
    	{
    		while (!hp.empty()) hp.pop();
    		for (l=ls[t]; l; l=a[l][1])
    		if (a[l][0]!=Fa)
    		hp.push(Add(b[a[l][0]]));
    		
    		b[t]=Xor(b[t],i);
    		if (!pd(b[t]))
    		b[t]=Xor(b[t],i);
    	}
    }
    
    void Dfs(int Fa,int t)
    {
    	int i;
    	for (i=ls[t]; i; i=a[i][1])
    	if (a[i][0]!=Fa && !bz[a[i][0]])
    	{
    		if (a[i][2]>mx) mx=a[i][2],mx2=i;
    		Dfs(t,a[i][0]);
    	}
    }
    
    int Ask(int x,int y) {int s;printf("? %d %d
    ",x,y);fflush(stdout);scanf("%d",&s);return s;}
    void Find(int x) {printf("! %d
    ",x);fflush(stdout);exit(0);}
    
    int main()
    {
    	#ifdef file
    	f1.open("CF1444E.in");
    	#endif
    	
    	p[0]=1;
    	fo(i,1,50) p[i]=p[i-1]*2;
    	#ifdef file
    	f1>>n,len=1;
    	fo(i,1,n-1) f1>>j,f1>>k,New(j,k),New(k,j),++D[j],++D[k];
    	#else
    	scanf("%d",&n),len=1;
    	fo(i,1,n-1) scanf("%d%d",&j,&k),New(j,k),New(k,j),++D[j],++D[k];
    	#endif
    	if (n-1<50) Inf.s2=p[n]-1;
    	else Inf.s1=p[n-50]-1,Inf.s2=p[50]-1;
    	f1.close();
    	
    	dfs(0,1);
    	fo(i,2,n) a[id[i]][2]=a[id[i]^1][2]=f[i];
    	x=1;
    	while (1)
    	{
    		mx=-1,Dfs(0,x);
    		if (mx==-1) Find(x);
    		
    		x=a[mx2][0],y=a[mx2^1][0];
    		z=Ask(x,y),bz[x]=bz[y]=1,bz[z]=0,x=z;
    	}
    	
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    dnn重置Host密码
    fiddle 网址过滤
    生成下面的模块时,启用了优化或没有调试信息
    关闭web.config的继承
    jQuery最佳实践
    坐标高速插入,移动和查询算法
    索引缓存方面的一些测试数据
    转帖微軟将从 .NET 4 以后的版本弃用 System.Data.OracleClient
    算法系列计数排序
    一款免费生成流程图的插件
  • 原文地址:https://www.cnblogs.com/gmh77/p/13956449.html
Copyright © 2011-2022 走看看