题目描述
题解
设每条边都有一个初始为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;
}