zoukankan      html  css  js  c++  java
  • CF1299D Around the World

    cf题面
    洛谷题面
    题解:
    将所有1连出的边(我们称之为“关键边”)删掉,得到若干个连通块。
    由于1号节点不会包含在 (geq) 4的环里,
    如果我们单独考虑一个连通块,那么它只可能是下面两种情况之一(这里套用了cf官方题解的图):


    现在考虑题目的限制条件:
    一个连通块满足条件的充要条件是:连通块里的所有环对应的边权异或和都能插入一个线性基里。
    证明?众所周知,线性基里的数亦或起来不能为0。(不知道的请百度
    然后再回过头来看上述的两种连通块:
    第一种:只有1条关键边,那么只有两种贡献:
    1.空的线性基(删掉);2.下面连通块得到的线性基(不删)。
    第二种:2条关键边,所以有3种情况:
    1.空的线性基(删掉);2.下面连通块得到的线性基(删一条边);
    3.下面连通块+包含红边的那个环得到的线性基。
    接下来考虑合并这些贡献。
    由于(w)在二进制下只有5位,本质不同的5位的线性基个数只有374个,
    所以我们可以把这些合法的线性基都预处理出来,并将每个线性基表示为一个状态。
    (f_{i,j}) 表示前(i)个连通块,合并后得到线性基(j)的方案数。
    转移时一个一个把贡献合并上去就好。(合并就是线性基的合并)
    注意到不同的线性基可能本质相同,我们需要让线性基唯一,这个可以参考一下我的insert函数:

    IN insert(int *a,int x){
    	FOR(i,4,0){
    		if(!x)return 0;
    		if(!((x>>i)&1))continue;
    		if(!a[i]){
    			a[i]=x;
    			FOR(j,i-1,0)if((a[i]>>j)&1)a[i]^=a[j];
    			F(j,i+1,4)if((a[j]>>i)&1)a[j]^=a[i];
    			return 1;
    		}
    		x^=a[i];
    	}
    	return 0;
    } 
    

    大概就是如果这一位是1并且有这一位的线性基,就把这个1消掉。
    DP数组可以滚动优化。
    时间复杂度:O(374* (log_w) +(n+m) (log_w) +374*n)
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    typedef long long ll;
    #define I inline void
    #define IN inline int
    #define C(x,y) memset(x,y,sizeof(x))
    #define STS system("pause")
    template<class D>I read(D &res){
    	res=0;register D g=1;register char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')g=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch)){
    		res=(res<<3)+(res<<1)+(ch^48);
    		ch=getchar();
    	}
    	res*=g;
    }
    const int Mod=1e9+7;
    IN insert(int *a,int x){
    	FOR(i,4,0){
    		if(!x)return 0;
    		if(!((x>>i)&1))continue;
    		if(!a[i]){
    			a[i]=x;
    			FOR(j,i-1,0)if((a[i]>>j)&1)a[i]^=a[j];
    			F(j,i+1,4)if((a[j]>>i)&1)a[j]^=a[i];
    			return 1;
    		}
    		x^=a[i];
    	}
    	return 0;
    } 
    struct B{
    	int b[6];
    	friend bool operator < (B x,B y){
    		F(i,0,4)if(x.b[i]!=y.b[i])return x.b[i]<y.b[i];
    		return 0;
    	}
    	friend bool operator == (B x,B y){
    		F(i,0,4)if(x.b[i]!=y.b[i])return 0;
    		return 1;
    	}
    }bas[440],now;
    map<B,int>mp;
    struct E{
    	int to,nt,w;
    }e[202000];
    #define T e[k].to
    int n,m,sn,ans,cnt,X,Y,W,tot,sum,c[440][440],val[101000],dep[101000],head[101000],t[6],vis[101000],f[101000][440];
    I Add(int &x,int y){
    	(x+=y)>=Mod?x-=Mod:0;
    }
    IN rnk(B a){
    	//cout<<mp[a]<<endl;
    	if(!mp[a]){
    		cout<<"!";
    		F(i,0,4)cout<<a.b[i]<<" ";
    		cout<<endl;
    		system("pause");
    	}
    	return mp[a];
    	re l=1,r=tot,mid;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(a==bas[mid])return mid;
    		if(a<bas[mid])r=mid-1;
    		else l=mid+1;
    	}
    	return mid;
    }
    IN Plus(B x,B y){
    	B tmp;C(tmp.b,0);
    	memcpy(tmp.b,x.b,sizeof(tmp.b));
    	F(i,0,4){
    		if(!y.b[i])continue;
    		if(!insert(tmp.b,y.b[i]))return 0;
    	}
    	return rnk(tmp);
    }
    I add(int x,int y,int w){
    	e[++sum].to=y;e[sum].nt=head[x];head[x]=sum;e[sum].w=w;
    }
    I D_1(int p,int w){
    	if(p==5){
    		tot++;F(i,0,4)bas[tot].b[i]=t[i];//,cout<<t[i]<<" ";
    		//cout<<endl;system("pause");
    		return;
    	}
    	F(i,0,(1<<p)-1)
    		if((w&i)==0){
    			t[p]=(1<<p)|i;
    			D_1(p+1,w|(1<<p));
    		}
    	t[p]=0;
    	D_1(p+1,w);
    }
    inline bool bbb(B x,B y){
    	re ca=0,cb=0;
    	F(i,0,4)ca+=(x.b[i]!=0),cb+=(y.b[i]!=0);
    	return ca<cb;
    }
    I init(){
    	tot=0;D_1(0,0);
    	sort(bas+1,bas+1+tot);F(i,1,tot)mp[bas[i]]=i;
    	F(i,1,tot)F(j,1,tot)c[i][j]=Plus(bas[i],bas[j]);
    }
    I D_2(int x,int fa,int depth,int V){
    	val[x]=V;dep[x]=depth;
    	for(re k=head[x];k!=-1;k=e[k].nt){
    		if(T==fa||vis[T]!=-1||T==1)continue;
    		if(dep[T]&&dep[x]<dep[T])sn&=insert(now.b,val[x]^val[T]^e[k].w);//cout<<"loop"<<(val[x]^val[T]^e[k].w)<<endl;
    		if(dep[T])continue;
    		D_2(T,x,depth+1,V^e[k].w);
    	}
    }
    I DP(int i,int rk){F(j,1,tot)Add(f[i][c[rk][j]],f[i-1][j]);}//F(j,1,tot)if(f[i][j])cout<<"!"<<rk<<" "<<i<<" "<<j<<endl;}
    int main(){
    	init();//cout<<tot<<endl;
    	read(n);read(m);C(head,-1);sum=-1;
    	F(i,1,m){
    		read(X);read(Y);read(W);add(X,Y,W);add(Y,X,W);
    	}
    	f[0][1]=1;C(vis,-1);
    	for(re k=head[1];k!=-1;k=e[k].nt)vis[T]=e[k].w;
    	cnt=0;
    	F(i,2,n){
    		if(vis[i]==-1)continue;//cout<<"@"<<endl;
    		C(now.b,0);X=0;W=0;cnt++;sn=1;
    		for(re k=head[i];k!=-1;k=e[k].nt)if(vis[T]!=-1){
    			X=T;W=e[k].w;break;
    		}
    		D_2(i,0,1,0);if(X)D_2(X,0,1,0);
    		DP(cnt,1);
    		//cout<<"sn="<<sn<<endl;
    		if(sn){
    			//F(i,0,4)cout<<now.b[i]<<" ";
    			//cout<<endl;
    			if(!X)DP(cnt,Y=rnk(now));//,cout<<Y<<endl;
    			else{
    				DP(cnt,Y=rnk(now));DP(cnt,rnk(now));//cout<<Y<<endl;
    				if(insert(now.b,vis[i]^vis[X]^W))DP(cnt,Y=rnk(now));//cout<<Y<<endl;
    			}
    		}
    		vis[i]=vis[X]=-1;
    	}
    	ans=0;
    	F(i,1,tot)Add(ans,f[cnt][i]);
    	cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    重新格式化部门表
    从不订购的客户
    回文数
    shell中的双括号表达式
    shell中的if语句
    shell
    view的生命周期
    shell中的数学运算
    shell中的expr命令
    shell中的退出状态码
  • 原文地址:https://www.cnblogs.com/Purple-wzy/p/12296609.html
Copyright © 2011-2022 走看看