绝世好题
神题,笔者并没有找到除官解外的题解,也并没有找到任何有人通过本题的证据(不过似乎是 2015 集训队作业),就来记录一下官方题解神仙做法,给个证明。
首先你发现有解答案上界是
然后?你不会了看了眼 tag 折半搜索,你尝试把点分成两部分,对两边预处理当前部分状态为
接下来就是很厉害的想法了。
官解告诉我们保留所有边,直接建反图从
它告诉我们只用预处理
很反直觉不是吗?
然而官解并没有给出证明,这里我们证明一下。
考虑我现在
所以我们在
参考代码:
#include<bits/stdc++.h>
#define ll long long
#define INF 214748364719260817ll
using namespace std;
ll t,n;
ll l[45],r[45];
ll to[45],td[45];
ll dep[45];
vector<ll>bk[45];
vector<ll>a,b;
struct px
{
ll nS,nu,c;
};
px dp[1<<18][18];
bool vis[1<<18][18];
px dfs(ll S,ll u)
{
if(to[u]!=1)return (px){S,u,0};
if(vis[S][td[u]])return dp[S][td[u]];
vis[S][td[u]]=1;
ll nu=u;
if((S>>td[u])&1)nu=r[u];
else nu=l[u];
dp[S][td[u]]=dfs(S^(1<<td[u]),nu);
++dp[S][td[u]].c;
return dp[S][td[u]];
}
ll get_ans(ll aS,ll bS,ll u)
{
if(u==n-1)return 0;
if(!to[u])return -INF;
if(to[u]==1)
return dp[aS][td[u]].c+get_ans(dp[aS][td[u]].nS,bS,dp[aS][td[u]].nu);
else
{
ll nu=u;
if((bS>>td[u])&1)nu=r[u];
else nu=l[u];
return 1+get_ans(aS,bS^(1<<td[u]),nu);
}
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin>>t;
for(ll sb=1;sb<=t;++sb)
{
cin>>n;
memset(to,0,sizeof(to));
memset(dep,0,sizeof(dep));
memset(vis,0,sizeof(vis));
a.clear();b.clear();
for(ll i=0;i<n;++i)bk[i].clear();
for(ll i=0;i<n-1;++i)cin>>l[i]>>r[i],--l[i],--r[i];
for(ll i=0;i<n-1;++i)bk[l[i]].emplace_back(i),bk[r[i]].emplace_back(i);
queue<ll>q;
q.emplace(n-1);
dep[n-1]=1;
while(!q.empty())
{
ll id=q.front();q.pop();
a.emplace_back(id);
for(auto it:bk[id])
{
if(!dep[it])
dep[it]=dep[id]+1,q.emplace(it);
}
}
cout<<"Case #"<<sb<<": ";
if(a.size()<2||!dep[0])
{
cout<<"Infinity\n";continue;
}
ll where=a.size()/2;
for(ll i=a.size()-1;i>=max(22ll,where);--i)b.emplace_back(a.back()),a.pop_back();
swap(a,b);
ll cot=0;
for(auto it:a)to[it]=1,td[it]=cot++;
cot=0;
for(auto it:b)to[it]=2,td[it]=cot++;
for(ll i=0;i<(1<<a.size());++i)
for(ll j=0;j<a.size();++j)
if(!vis[i][j])
dfs(i,a[j]);
ll res=get_ans(0,0,0);
if(res<0)cout<<"Infinity\n";
else cout<<res<<'\n';
}
}