zoukankan      html  css  js  c++  java
  • P4426 [HNOI/AHOI2018]毒瘤

    挺不错的一个题。

    题意即为求一个图的独立集方案数。

    如果原图是一棵树,可以直接大力f[x][0/1]来dp。

    由于非树边很少,考虑2^11容斥,强制某些点必选,然后再O(n)dp,这样应该过不了。

    发现这个容斥本质上是对一些点进行修改,修改的形式是强制它必须选。

    直接xjb上一个ddp就没了。

    这里由于有可能会/0,考虑维护一下结尾0的个数就行。

    复杂度的话,直接乱写是311*log2的,但是实际上我们可以优化加点和删点的顺序。

    考虑按照某一个特定的顺序去容斥。

    我的想法是一个简单的贪心,每次找一个新加入的尽可能少的状态,复杂度玄学。

    比较优秀的做法是dfs来构造集合,这样的修改总量是2^n的,十分优秀。

    #include<bits/stdc++.h>
    #define N 220000
    #define eps 1e-7
    #define inf 1e9+7
    #define db double
    #define ll long long
    #define ldb long double
    using namespace std;
    inline int read()
    {
    	char ch=0;
    	int x=0,flag=1;
    	while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return x*flag;
    }
    const int mo=998244353;
    int ksm(int x,int k)
    {
    	int ans=1;
    	while(k)
    	{
    		if(k&1)ans=1ll*ans*x%mo;
    		k>>=1;x=1ll*x*x%mo; 
    	}
    	return ans;
    }
    int inv(int x){return ksm(x,mo-2);}
    struct node
    {
    	int x,y;
    	int w(){return y?0:x;}
    };
    node operator*(node a,int b)
    {
    	node ans=a;
    	if(b)ans.x=1ll*ans.x*b%mo;else ans.y++;
    	return ans;
    }
    node operator/(node a,int b)
    {
    	node ans=a;
    	if(b)ans.x=1ll*ans.x*inv(b)%mo;else ans.y--;
    	return ans;
    }
    struct matrix
    {
    	int s[2][2];
    	void clear()
    	{
    		memset(s,0,sizeof(s));
    	}
    };
    matrix operator*(matrix a,matrix b)
    {
    	matrix ans;ans.clear();
    	for(int i=0;i<2;i++)
    	{
    		for(int j=0;j<2;j++)for(int k=0;k<2;k++)
    		ans.s[i][j]=(ans.s[i][j]+(1ll*a.s[i][k]*b.s[k][j]%mo))%mo;
    	}
    	return ans;
    }
    struct edge{int to,nxt;}e[N*2];
    int num,f[N],head[N];
    inline void add(int x,int y){e[++num]={y,head[x]};head[x]=num;}
    int find(int x){if(x!=f[x])f[x]=find(f[x]);return f[x];}
    bool merge(int x,int y){x=find(x);y=find(y);if(x==y)return false;f[x]=y;return true;}
    struct link{int x,y;}q[N];
    bool vis[N];
    node a[N][2],b[N][2];
    int times,p[N],d[N],id[N],fs[N],sz[N],fa[N],son[N],top[N],low[N],flag[N];
    struct Segment_Tree
    {
    	#define lson o<<1
    	#define rson o<<1|1
    	#define mid ((l+r)>>1)
    	matrix dp[N*4];
    	inline void pushup(int o){dp[o]=dp[lson]*dp[rson];}
    	void build(int o,int l,int r)
    	{
    		if(l==r)
    		{
    			int x=p[l];
    			dp[o]={{{b[x][0].w(),b[x][0].w()},{b[x][1].w(),0}}};
    			return;
    		}
    		build(lson,l,mid);build(rson,mid+1,r);pushup(o);
    	}
    	void optset(int o,int l,int r,int q)
    	{
    		if(l==r)
    		{
    			int x=p[l];
    			dp[o]={{{b[x][0].w(),b[x][0].w()},{b[x][1].w(),0}}};
    			return;
    		}
    		if(q<=mid)optset(lson,l,mid,q);
    		else optset(rson,mid+1,r,q);
    		pushup(o);
    	}
    	matrix query(int o,int l,int r,int ql,int qr)
    	{
    		if(ql<=l&&r<=qr)return dp[o];
    		if(ql<=mid&&qr>mid)return query(lson,l,mid,ql,qr)*query(rson,mid+1,r,ql,qr);
    		if(ql<=mid)return query(lson,l,mid,ql,qr);
    		if(qr>mid)return query(rson,mid+1,r,ql,qr);
    	}
    }T;
    matrix get(int x){return T.query(1,1,times,id[x],id[low[top[x]]]);}
    void update(int x,int o)
    {
    	b[x][0]=b[x][0]/flag[x];flag[x]=o;b[x][0]=b[x][0]*flag[x];
    	while(true)
    	{
    		matrix A=get(top[x]);
    		T.optset(1,1,times,id[x]);
    		matrix B=get(top[x]);
    		
    		x=fa[top[x]];if(!x)return;
    		
    		b[x][0]=b[x][0]/((A.s[0][0]+A.s[1][0])%mo);
    		b[x][0]=b[x][0]*((B.s[0][0]+B.s[1][0])%mo);
    		b[x][1]=b[x][1]/A.s[0][0];b[x][1]=b[x][1]*B.s[0][0];
    	}
    }
    void dfs1(int x)
    {
    	sz[x]=1;
    	for(int i=head[x];i!=-1;i=e[i].nxt)
    	{
    		int to=e[i].to;
    		if(to==fa[x])continue;
    		fa[to]=x;dfs1(to);sz[x]+=sz[to];
    		if(sz[son[x]]<sz[to])son[x]=to;
    	}
    }
    void dfs2(int x,int tp)
    {
    	p[++times]=x;id[x]=times;top[x]=tp;
    	if(son[x])dfs2(son[x],tp);else low[tp]=x;
    	for(int i=head[x];i!=-1;i=e[i].nxt)
    	{
    		int to=e[i].to;
    		if(top[to])continue;
    		dfs2(to,to);
    	}
    }
    void DP(int x)
    {
    	a[x][0]=a[x][1]=b[x][0]=b[x][1]={1,0};
    	for(int i=head[x];i!=-1;i=e[i].nxt)
    	{
    		int to=e[i].to;
    		if(to==fa[x])continue;
    		DP(to);
    		a[x][0].x=1ll*a[x][0].x*(a[to][0].x+a[to][1].x)%mo;
    		a[x][1].x=1ll*a[x][1].x*a[to][0].x%mo;
    		if(to==son[x])continue;
    		b[x][0].x=1ll*b[x][0].x*(a[to][0].x+a[to][1].x)%mo;
    		b[x][1].x=1ll*b[x][1].x*a[to][0].x%mo;
    	}
    }
    int main()
    {
    	int n=read(),m=read(),cnt=0;
    	num=-1;memset(head,-1,sizeof(head));
    	for(int i=1;i<=n;i++)f[i]=i;
    	for(int i=1;i<=m;i++)
    	{
    		int x=read(),y=read();
    		if(merge(x,y))add(x,y),add(y,x);
    		else q[++cnt]={x,y};
    	}	
    	dfs1(1);dfs2(1,1);DP(1);T.build(1,1,times);
    	for(int i=1;i<=n;i++)flag[i]=1;
    	for(int i=0;i<(1<<cnt);i++)fs[i]=fs[i>>1]+(i&1);
    	int ans=0;
    	for(int o=0,lst=0;o<(1<<cnt);o++)
    	{
    		int s=-1;
    		for(int i=0;i<(1<<cnt);i++)
    		if(!vis[i])if(s==-1||fs[i^lst]<fs[s^lst])s=i;
    		for(int i=1;i<=cnt;i++)if((1<<(i-1))&(s^lst))
    		{
    			int x=q[i].x,y=q[i].y;
    			if(1<<(i-1)&s)
    			{
    				if(d[x]++==0)update(x,0);
    				if(d[y]++==0)update(y,0);
    			}
    			else
    			{
    				if(d[x]--==1)update(x,1);
    				if(d[y]--==1)update(y,1);
    			}
    		}
    		vis[s]=true;lst=s;
    		matrix t=get(1);
    		if(fs[s]%2==0)ans=(ans+((t.s[0][0]+t.s[1][0])%mo))%mo;
    		else ans=(ans-((t.s[0][0]+t.s[1][0])%mo))%mo;
    	}
    	printf("%d",(ans%mo+mo)%mo);
    	return 0;
    }
    
  • 相关阅读:
    Quartz.Net 作业调度后台管理系统,基于Extjs
    [备份]EntityFramework
    WebMisSharp升级说明,最新版本1.6.0
    AllPay(欧付宝)支付接口集成
    Paypal Rest Api自定义物流地址(跳过填写物流地址)
    根据IP获取国家
    ViewBag 找不到编译动态表达式所需的一种或多种类型,是否缺少引用?
    Extjs4 DateTimeField,日期时间控件完美版
    IOS Swift 训练
    .Net集成PayPal的Demo
  • 原文地址:https://www.cnblogs.com/Creed-qwq/p/10629929.html
Copyright © 2011-2022 走看看