Examples

CF869D The Overdosing Ubiquity 解题报告

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;
}
posted @ 2022-01-09 16:42  xiaoziyao  阅读(61)  评论(0编辑  收藏  举报