zoukankan      html  css  js  c++  java
  • 【HNOI2018】毒瘤

    【HNOI2018】毒瘤

    img
    img
    img
    img

    (f_{v,0})表示(v)的子树中(v)不选的方案数,(f_{v,1})表示(v)选的方案数。

    显然

    [f_{v,0}=prod (f_{sn,0}+f_{sn,1})\ f_{v,1}=prod f_{sn,0} ]

    我们可以写成矩阵乘法的形式

    [egin{bmatrix}f_{sn,0}& f_{sn,1}end{bmatrix} egin{bmatrix} f_{v,0}&f_{v,1}\f_{v,0}& 0 end{bmatrix}=egin{bmatrix}f_{v,0}& f_{v,1}end{bmatrix} ]

    然后我们就枚举非树边两端的点选与不选,用动态(DP)维护。

    设非树边有(k)条,暴力枚举复杂度(O(2^{2k}log^2N))

    但是我们发现,每条边只需要枚举其中一个点就好了。如果枚举为不选,那么另一个点就没有限制;如果必选,那么另一个点就不选。复杂度(O(2^{k}log^2N))

    和【SDOI 2017】切树游戏一样要用一个类来处理除法中除(0)的情况。

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define N 100005
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    const ll mod=998244353;
    ll ksm(ll t,ll x) {
    	ll ans=1;
    	for(;x;x>>=1,t=t*t%mod)
    		if(x&1) ans=ans*t%mod;
    	return ans;
    }
    
    int n,m;
    struct road {
    	int to,next;
    }s[N<<1];
    int h[N],cnt;
    void add(int i,int j) {s[++cnt]=(road) {j,h[i]};h[i]=cnt;}
    struct edge {
    	int x,y;
    }e[N];
    int tot;
    int FA[N];
    int Getf(int v) {return FA[v]==v?v:FA[v]=Getf(FA[v]);}
    
    int fa[N],size[N],son[N];
    int top[N];
    struct info {
    	ll a,z;
    	info() {a=1,z=0;}
    	info(int x,int y) {a=x,z=y;}
    	ll val() {return z?0:a;}
    };
    
    info operator *(info x,ll y) {
    	if(!y) x.z++;
    	else x.a=x.a*y%mod;
    	return x;
    }
    
    info operator /(info x,ll y) {
    	if(!y) x.z--;
    	else x.a=x.a*ksm(y,mod-2)%mod;
    	return x;
    }
    
    struct matrix {
    	int a[2][2];
    	void Init() {a[0][0]=a[0][1]=a[1][0]=a[1][1]=0;}
    };
    
    matrix operator *(const matrix &x,const matrix &y) {
    	matrix tem;
    	tem.Init();
    	tem.a[0][0]=(1ll*x.a[0][0]*y.a[0][0]+1ll*x.a[0][1]*y.a[1][0])%mod;
    	tem.a[0][1]=(1ll*x.a[0][0]*y.a[0][1]+1ll*x.a[0][1]*y.a[1][1])%mod;
    	tem.a[1][0]=(1ll*x.a[1][0]*y.a[0][0]+1ll*x.a[1][1]*y.a[1][0])%mod;
    	tem.a[1][1]=(1ll*x.a[1][0]*y.a[0][1]+1ll*x.a[1][1]*y.a[1][1])%mod;
    	return tem;
    }
    
    struct tree {
    	int l,r;
    	matrix w;
    }tr[N<<2];
    void update(int v) {tr[v].w=tr[v<<1|1].w*tr[v<<1].w;}
    
    void build(int v,int l,int r) {
    	tr[v].l=l,tr[v].r=r;
    	if(l==r) return ;
    	int mid=l+r>>1;
    	build(v<<1,l,mid),build(v<<1|1,mid+1,r);
    }
    
    void dfs(int v) {
    	size[v]=1;
    	for(int i=h[v];i;i=s[i].next) {
    		int to=s[i].to;
    		if(to==fa[v]) continue ;
    		fa[to]=v;
    		dfs(to);
    		size[v]+=size[to];
    		if(size[son[v]]<size[to]) son[v]=to;
    	}
    }
    
    matrix query(int v,int l,int r) {
    	if(l<=tr[v].l&&tr[v].r<=r) return tr[v].w;
    	int mid=tr[v].l+tr[v].r>>1;
    	if(r<=mid) return query(v<<1,l,r);
    	else if(l>mid) return query(v<<1|1,l,r);
    	else return query(v<<1|1,l,r)*query(v<<1,l,r);
    }
    
    int dfn[N],lst[N],id;
    int bot[N];
    ll t[N];
    info G[N][2];
    
    void Modify(int v,int p) {
    	if(tr[v].l>p||tr[v].r<p) return ;
    	if(tr[v].l==tr[v].r) {
    		matrix &w=tr[v].w;
    		w.a[0][0]=G[lst[p]][0].val(),w.a[0][1]=G[lst[p]][1].val();
    		w.a[1][0]=G[lst[p]][0].val(),w.a[1][1]=0;
    		if(~t[lst[p]]) {
    			w.a[0][t[lst[p]]^1]=w.a[1][t[lst[p]]^1]=0;
    		}
    		return ;
    	}
    	Modify(v<<1,p),Modify(v<<1|1,p);
    	update(v);
    }
    
    void dfs2(int v,int tp) {
    	dfn[v]=++id;
    	lst[id]=v;
    	top[v]=tp;
    	bot[v]=v;
    	if(son[v]) {
    		dfs2(son[v],tp);
    		bot[v]=bot[son[v]];
    	}
    	G[v][0]=G[v][1]=info(1,0);
    	for(int i=h[v];i;i=s[i].next) {
    		int to=s[i].to;
    		if(to==son[v]||to==fa[v]) continue ;
    		dfs2(to,to);
    		matrix tem=query(1,dfn[to],dfn[bot[to]]);
    		G[v][0]=G[v][0]*(tem.a[0][0]+tem.a[0][1]);
    		G[v][1]=G[v][1]*tem.a[0][0];
    	}
    	Modify(1,dfn[v]);
    }
    
    int dep;
    void Confirm(int v,int flag) {
    	dep++;
    	t[v]=flag;
    	matrix tem;
    	for(int i=top[v];fa[i];i=top[fa[i]]) {
    		tem=query(1,dfn[i],dfn[bot[i]]);
    		G[fa[i]][0]=G[fa[i]][0]/(tem.a[0][0]+tem.a[0][1]);
    		G[fa[i]][1]=G[fa[i]][1]/tem.a[0][0];
    	}
    	Modify(1,dfn[v]);
    	for(int i=top[v];fa[i];i=top[fa[i]]) {
    		tem=query(1,dfn[i],dfn[bot[i]]);
    		G[fa[i]][0]=G[fa[i]][0]*(tem.a[0][0]+tem.a[0][1]);
    		G[fa[i]][1]=G[fa[i]][1]*tem.a[0][0];
    		Modify(1,dfn[fa[i]]);
    	}
    }
    
    vector<int>st;
    vector<int>E[N];
    int ban[N];
    ll ans;
    
    vector<int>ea,eb;
    void solve(int now) {
    	if(now==ea.size()) {
    		matrix tem=query(1,dfn[1],dfn[bot[1]]);
    		(ans+=tem.a[0][0]+tem.a[0][1])%=mod;
    		return ;
    	}
    	int x=ea[now],y=eb[now];
    	if(t[x]==0||t[x]==-1) {
    		int pre=t[x];
    		Confirm(x,0);
    		solve(now+1);
    		Confirm(x,pre);
    	}
    	if((t[x]==1||t[x]==-1)&&(t[y]==0||t[y]==-1)) {
    		int prex=t[x],prey=t[y];
    		Confirm(x,1),Confirm(y,0);
    		solve(now+1);
    		Confirm(x,prex),Confirm(y,prey);
    	}
    }
    
    int f[N][2];
    void DP(int v,int fr) {
    	f[v][0]=f[v][1]=1;
    	for(int i=h[v];i;i=s[i].next) {
    		int to=s[i].to;
    		if(to==fr) continue ;
    		DP(to,v);
    		f[v][0]=f[v][0]*(f[to][0]+f[to][1])%mod;
    		f[v][1]=f[v][1]*f[to][0]%mod;
    	}
    }
    
    int main() {
    	memset(t,-1,sizeof(t));
    	n=Get(),m=Get();
    	int a,b;
    	for(int i=1;i<=n;i++) FA[i]=i;
    	for(int i=1;i<=m;i++) {
    		a=Get(),b=Get();
    		if(Getf(a)==Getf(b)) {
    			e[++tot]=(edge) {a,b};
    			ea.push_back(a);
    			eb.push_back(b);
    			st.push_back(a);
    			st.push_back(b);
    			E[a].push_back(b);
    			E[b].push_back(a);
    		} else {
    			add(a,b),add(b,a);
    			FA[Getf(a)]=Getf(b);
    		}
    	}
    	sort(st.begin(),st.end());
    	int cc=unique(st.begin(),st.end())-st.begin();
    	while(st.size()>cc) st.pop_back();
    	build(1,1,n);
    	dfs(1);
    	dfs2(1,1);
    	solve(0);
    	cout<<ans;
    	return 0;
    }
    
    
  • 相关阅读:
    kotlin 通过 下标比对
    textarea元素调整
    jquery给两个标签绑定一个事件
    开发过程中遇到的错误
    response.setHeader各种用法详解
    如何在eclipse里删除一个类 然后SVN服务器也同时删了这个类
    @pathvariable 与@requestparam 写rest接口时遇到的
    $.getJSON
    easyUI学习
    jQuery validator addMethod 动态提示信息
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10532591.html
Copyright © 2011-2022 走看看