CF869D The Overdosing Ubiquity 解题报告:
题意
给定一个 \(n\) 个结点的完全二叉树,\(k\) 号点的父亲为 \(\lfloor\frac{k}{2}\rfloor\),新加 \(m\) 条边,求本质不同简单路径数量。
\(1\leqslant n\leqslant 10^9,0\leqslant m\leqslant 4\)。
分析
给一个常数大,码量大但好想的做法。
首先不经过新加边的路径数量就是 \(O(n^2)\),然后考虑强制经过新加边的方案数。
由于 \(m\) 很小,我们可以枚举选哪些边,枚举走边的顺序,枚举边的方向(也就是无向边变成有向边),然后考虑按照顺序依次经过钦定的边的路径数量。
我们令 \((x_i,y_i)\) 为选择的第 \(i\) 条新加边(一共 \(k\) 条),取出两条路径 \(A\) 和 \(B\)。
\(A\):从 \(y_1\) 沿着树上的边走到 \(x_2\),然后走 \((x_2,y_2)\),再从 \(y_2\) 沿着树上的边走到 \(x_3\)……
\(B\): \(x_1\) 沿着树上的边走到 \(y_k\) 的路径。
若 \(A\) 与 \(B\) 有交,那么可以发现经过这些钦定边的路径的起点一定是 \(x\) 不经过 \(A\) 内点沿着树上的边能到达的点,同理终点一定是 \(y\) 不经过 \(B\) 内点沿着树上的边能到达的点。
这个东西可以直接在树上搜一遍得到,也可以发现这个连通块一定是一个子树去掉若干子树的结果,大子树和去掉的子树都可以枚举 \(A\) 上的点计算出来,具体可以看代码。(\(\text{dfs}\) 部分)
若 \(A\) 与 \(B\) 没有交,那么可以发现我们几乎可以在 \(A\) 与 \(B\) 到达的点中任选两个点作为起点和终点,但是这两个点 \(s,t\) 要满足 \((x_1,s)\) 与 \((y_k,t)\) 无交
,我们枚举 \(B\) 上所有点,令枚举的点为 \(s\) 的祖先,那么 \(t\) 一定在 \(B\) 更后面的点的子树中,于是前缀和一下就做完了。
然后你就写出了大分讨代码,接下来就只需要足够的耐心调试了!
时间复杂度 \(O((m\log n)^3m!4^m)\),非常松,随便过。
代码
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<map>
using namespace std;
const int maxn=1000005,mod=1000000007;
int n,m,ans;
int x[maxn],y[maxn],t[maxn],p[maxn],q[maxn],aa[maxn],bb[maxn],cc[maxn],dd[maxn],no[maxn];
map<int,int>vis;
int getsize(int x){
// printf("getsize(%d)=",x);
int r=x,dep=1;
while((r<<1|1)<=n)
r=(r<<1|1),dep++;
int out=(r<<1|1);
// printf("%d\n",((1<<dep)-1)+max((1<<dep)-(out-n),0));
return ((1<<dep)-1)+max((1<<dep)-(out-n),0);
}
int isfa(int x,int y){
while(y){
if(x==y)
return 1;
y>>=1;
}
return 0;
}
void dfs(int x,int ban1,int ban2,int &c){
int nos=0;
for(map<int,int>::iterator it=vis.begin();it!=vis.end();it++)
if(it->second&&it->first!=x)
no[++nos]=it->first;
if(ban1&&vis[ban1]==0&&ban1!=x)
no[++nos]=ban1;
if(ban2&&vis[ban2]==0&&ban2!=x)
no[++nos]=ban2;
sort(no+1,no+1+nos);
// for(int i=1;i<=nos;i++)
// printf("%d%c",no[i],i==nos? '\n':' ');
int fa=0;
for(int i=1;i<=nos;i++)
if(no[i]&&no[i]<x&&isfa(no[i],x))
fa=max(fa,no[i]);
int y=x,lst=0;
while(y!=fa)
lst=(y&1),y>>=1;
fa=(fa<<1)|lst,c=getsize(fa);
for(int i=1;i<=nos;i++)
if(no[i]>fa&&isfa(fa,no[i]))
for(int j=i+1;j<=nos;j++)
if(no[j]&&no[j]>fa&&isfa(no[i],no[j]))
no[j]=0;
for(int i=1;i<=nos;i++)
if(no[i]&&fa<no[i]&&isfa(fa,no[i])){
// printf("i=%d no[i]=%d %d\n",i,no[i],getsize(no[i]));
c-=getsize(no[i]);
}
// printf("y=%d fa=%d\n",y,fa);
// printf("(dfs %d %d %d)=%d(fa=%d)\n",x,ban1,ban2,c,fa);
/*if(x==ban||x>n||x<1)
return ;
vis[x]=1,c++;
if(vis[x>>1]==0)
dfs(x>>1,ban,c);
if(vis[x<<1]==0)
dfs(x<<1,ban,c);
if(vis[x<<1|1]==0)
dfs(x<<1|1,ban,c); */
}
int main(){
scanf("%d%d",&n,&m),ans=1ll*n*n%mod;
if(m==0){
printf("%d\n",ans);
return 0;
}
for(int i=1;i<=m;i++)
scanf("%d%d",&x[i],&y[i]);
for(int i=1;i<(1<<m);i++){
int tot=0;
for(int j=1;j<=m;j++)
if((i>>(j-1))&1)
t[++tot]=j,p[tot]=tot;
do{
for(int j=1;j<=tot;j++)
q[j]=t[p[j]];
for(int j=0;j<(1<<tot);j++){
// printf("i=%d j=%d ans=%d\n",i,j,ans);
vis.clear();
for(int k=1;k<=tot;k++)
vis[x[q[k]]]=vis[y[q[k]]]=1;
for(int k=1;k<=tot;k++)
if((j>>(k-1))&1)
swap(x[q[k]],y[q[k]]);
int flg=0;
for(int k=1;k<=tot;k++)
for(int r=k+1;r<=tot;r++)
if(x[q[k]]==x[q[r]]||y[q[k]]==y[q[r]]||x[q[k]]==y[q[r]]||(y[q[k]]==x[q[r]]&&r!=k+1))
flg=1;
for(int k=1;k<tot;k++){
int X=y[q[k]],Y=x[q[k+1]],a=__builtin_clz(X),b=__builtin_clz(Y),s=X,t=Y;
while(a<b){
s>>=1,a++;
if(s!=t)
flg|=vis[s],vis[s]=1;
}
while(b<a){
t>>=1,b++;
if(s!=t)
flg|=vis[t],vis[t]=1;
}
while(s!=t){
s>>=1,a++,flg|=vis[s],vis[s]=1;
t>>=1,b++;
if(s!=t)
flg|=vis[t],vis[t]=1;
}
}
// for(int k=1;k<=tot;k++)
// printf("xx=%d yy=%d\n",x[k],y[k]);
if(flg==0){
int X=x[q[1]],Y=y[q[tot]],a=__builtin_clz(X),b=__builtin_clz(Y),s=X,t=Y,f=0,as=0,bs=0,cs=0;
while(a<b){
s>>=1,a++;
if(s!=t)
f|=vis[s],aa[++as]=s;
}
while(b<a){
t>>=1,b++;
if(s!=t)
f|=vis[t],bb[++bs]=t;
}
while(s!=t){
s>>=1,a++,f|=vis[s],aa[++as]=s;
t>>=1,b++;
if(s!=t)
f|=vis[t],bb[++bs]=t;
}
if(f){
// puts("case1");
int c0=0,c1=0;
dfs(X,0,0,c0),dfs(Y,0,0,c1);
ans=(ans+1ll*c0*c1)%mod;
}
else{
// puts("case2");
for(int k=1;k<=as;k++)
cc[++cs]=aa[k];
for(int k=bs;k>=1;k--)
cc[++cs]=bb[k];
// printf("X=%d Y=%d\n",X,Y);
cc[0]=X,cc[cs+1]=Y;
/* puts("CC");
for(int k=0;k<=cs+1;k++)
printf("%d%c",cc[k],k==cs+1? '\n':' ');*/
int all=0;
// puts("A");
dd[0]=0,dfs(X,0,cc[1],dd[0]),all+=dd[0];
for(int k=1;k<=cs;k++)
dd[k]=0,dfs(cc[k],cc[k-1],cc[k+1],dd[k]),all+=dd[k];
// puts("B");
dd[cs+1]=0,dfs(Y,0,cc[cs],dd[cs+1]),all+=dd[cs+1];
/* for(int k=0;k<=cs+1;k++)
printf("k=%d dd[k]=%d\n",k,dd[k]);*/
for(int k=0;k<=cs;k++)
all-=dd[k],ans=(ans+1ll*dd[k]*all)%mod;
}
}
// printf("i=%d j=%d ans=%d\n",i,j,ans);
for(int k=1;k<=tot;k++)
if((j>>(k-1))&1)
swap(x[q[k]],y[q[k]]);
}
}while(next_permutation(p+1,p+1+tot));
// printf("i=%d ans=%d\n",i,ans);
}
printf("%d\n",ans);
return 0;
}