bzoj 5287 [Hnoi2018]毒瘤

http://www.elijahqi.win/archives/3563
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5287

1、树简单dp环简单讨论

#include<cstdio>
#include<cctype>
#include<vector>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int mod=998244353;
int n,m;
namespace sol1{
    const int N=1e5+10;
    struct node{
        int y,next;
    }data[N<<1];
    int h[N],num,dp[N][2];
    inline void dfs(int x,int fa){dp[x][0]=dp[x][1]=1;
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y;if(y==fa) continue;dfs(y,x);
            dp[x][0]=(ll)dp[x][0]*(dp[y][0]+dp[y][1])%mod;
            dp[x][1]=(ll)dp[x][1]*dp[y][0]%mod;
        }
    }
    inline void gao(){
        for (int i=1;i<=m;++i){
            int x=read(),y=read();
            data[++num].y=y;data[num].next=h[x];h[x]=num;
            data[++num].y=x;data[num].next=h[y];h[y]=num;
        }dfs(1,1);dp[1][0]+=dp[1][1];dp[1][0]%=mod;
        printf("%d\n",dp[1][0]);
    }
}
namespace sol2{
    const int N=50;
    struct node{
        int x,y;
    }nd[N];
    inline void gao(){
        for (int i=1;i<=m;++i){
            nd[i].x=read()-1;nd[i].y=read()-1;  
        }int ans=0;
        for (int s=0;s<(1<<n);++s){bool flag=1;
            for (int i=1;i<=m;++i){
                if (((1<<nd[i].x)&s)&&((1<<nd[i].y)&s)) {flag=0;break;}
            }if(flag) ++ans;
        }printf("%d\n",ans);
    }
}
namespace sol3{
    const int N=1e5+10;
    struct node{
        int y,next;
    }data[N<<1];
    int h[N],num,dp[N][2];bool visit[N],mark[N];
    inline void dfs(int x,int fa){dp[x][0]=dp[x][1]=1;
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y;if(y==fa) continue;dfs(y,x);
            dp[x][0]=(ll)dp[x][0]*(dp[y][0]+dp[y][1])%mod;
            dp[x][1]=(ll)dp[x][1]*dp[y][0];
        }
    }
    inline bool dfs1(int x,int fa){visit[x]=1;
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y;if (y==fa) continue;
            if (visit[y]) {mark[x]=1;return 1;}
            if(dfs1(y,x)) {mark[x]=1;return 1;}
        }return 0;
    }vector<int> pd;
    inline void dfs2(int x,int fa){
        if (mark[x]) pd.push_back(x);visit[x]=1;
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y;if (y==fa) continue;
            if (mark[y]&&!visit[y]) dfs2(y,x);
        }
    }
    inline void gao(){
        for (int i=1;i<=m;++i){
            int x=read(),y=read();
            data[++num].y=y;data[num].next=h[x];h[x]=num;
            data[++num].y=x;data[num].next=h[y];h[y]=num;
        }dfs1(1,1);int st=0;memset(visit,0,sizeof(visit));
        for (int i=1;i<=n;++i) if (mark[i]) {st=i;break;}dfs2(st,st);
        for (int i=0;i<pd.size();++i) {
            int x=pd[i];dp[x][0]=dp[x][1]=1;
            for (int j=h[x];j;j=data[j].next){
                int y=data[j].y;if (mark[y]) continue;
                dfs(y,x);
                dp[x][0]=(ll)dp[x][0]*(dp[y][0]+dp[y][1])%mod;
                dp[x][1]=(ll)dp[x][1]*dp[y][0]%mod;
            }
        }int ans=0,tmp0=0,tmp1=0;
        //for (int i=0;i<pd.size();++i) {int x=pd[i];printf("%d %d\n",dp[x][0],dp[x][1]);}
        tmp0=dp[pd[0]][0];tmp1=dp[pd[0]][1];tmp1=0;
        for (int i=1;i<pd.size();++i){
            int x=pd[i],t0=tmp0,t1=tmp1;
            tmp0=(ll)dp[x][0]*(t0+t1)%mod;
            tmp1=(ll)dp[x][1]*t0%mod;
        }ans=(tmp0+tmp1)%mod;
        tmp0=dp[pd[0]][0];tmp1=dp[pd[0]][1];tmp0=0;
        for (int i=1;i<pd.size();++i){
            int x=pd[i],t0=tmp0,t1=tmp1;
            tmp0=(ll)dp[x][0]*(t0+t1)%mod;
            tmp1=(ll)dp[x][1]*t0%mod;
        }ans+=tmp0;ans%=mod;printf("%d\n",ans);
    }
}
int main(){
    freopen("duliu.in","r",stdin);
    freopen("duliu.out","w",stdout);
    n=read();m=read();
    if(n<=20) {sol2::gao();return 0;}
    if(m==n-1){
        sol1::gao();return 0;
    }
    if (m==n){sol3::gao();return 0;}
    return 0;
}

2、发现我们这个返祖边很少于是可以3^(m-n)来枚举 但是显然这样多余了 可以发现状态一共是(0,1)(1,0)(0,0) 对于一对返祖边来说只有这些状态 接着又发现(0,1)(0,0) 其实i是一种状态 于是2^(m-n)枚举再暴力dp即可

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int mod=998244353;
const int N=1e5+10;
inline int inc(int x,int v){return x+v>=mod?x+v-mod:x+v;}
struct node{
    int y,next;
}data[N<<1];
int h[N],fr[N],to[N],num,cnt,dfn[N],dp[N][2],n,m,bin[20];bool visit[N],mark[N][2];
inline void dfs_init(int x,int fa){
    dfn[x]=++num;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (y==fa) continue;
        if (!dfn[y]) dfs_init(y,x);
        else if(dfn[y]<dfn[x]) to[++cnt]=x,fr[cnt]=y;
    }
}
inline void dfs(int x){
    dp[x][0]=!mark[x][0];dp[x][1]=!mark[x][1];visit[x]=1;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (visit[y]) continue;dfs(y);
        dp[x][0]=(ll)dp[x][0]*(dp[y][0]+dp[y][1])%mod;
        dp[x][1]=(ll)dp[x][1]*dp[y][0]%mod;
    }
}
int main(){
    freopen("bzoj5287.in","r",stdin);
    n=read();m=read();bin[1]=1;
    for (int i=1;i<=m;++i){
        int x=read(),y=read();
        data[++num].y=y;data[num].next=h[x];h[x]=num;
        data[++num].y=x;data[num].next=h[y];h[y]=num;
    }num=0;dfs_init(1,1);int S=(1<<cnt)-1,ans=0;
    for (int i=2;i<=cnt;++i) bin[i]=1<<i-1;
    for (int s=0;s<=S;++s){
        memset(mark,0,sizeof(mark));
        for (int i=1;i<=cnt;++i){
            if (bin[i]&s) mark[fr[i]][0]=1,mark[to[i]][1]=1;
            else mark[fr[i]][1]=1;
        }memset(visit,0,sizeof(visit));
        dfs(1);ans=inc(ans,inc(dp[1][1],dp[1][0]));
    }printf("%d\n",ans);
    return 0;
}

3、发现这样过不去 为什么 因为暴力dp的时候有很多重复的状态

怎么办 考虑将那些被返祖边包含的点提取出来 建虚树

提前预处理出dp转移时候的系数 然后再dp的时候只需要这虚树上的有限的点即可

设k[x][0] k[x][1]分别表示x节点 状态是0或者1 他们对于下一个虚树节点的转移系数分别是多少tr0,tr1就是真正虚树的下一个节点到我这里的转移了

遇到不是虚树上的节点 就把他的系数加进去 遇到虚树节点 就重置

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int N=1e5+10;
const int mod=998244353;
struct node{
    int y,next;
}data[N<<1],data1[N];
inline int inc(int x,int v){return x+v>=mod?x+v-mod:x+v;} 
struct node1{
    int x,y;
    inline node1 operator +(const node1 &a){
        return (node1){inc(a.x,x),inc(a.y,y)};
    }
    inline node1 operator *(ll a){
        return (node1){x*a%mod,y*a%mod};
    }
}k[N][2],tr1[N],tr0[N];
int dfn[N],num,h[N],h1[N],fr[N],to[N],g[N][2],dp[N][2],cnt,n,m,bin[30],size[N];
bool mk[N][2],visit[N],mark[N];
inline void dfs_init(int x,int fa){
    dfn[x]=++num;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (y==fa) continue;
        if(!dfn[y]) dfs_init(y,x),size[x]+=size[y];
        else if(dfn[y]<dfn[x]) fr[++cnt]=y,to[cnt]=x,mark[x]=mark[y]=1;
    }mark[x]|=(size[x]>=2);size[x]=mark[x]?1:size[x];
}
inline void insert1(int x,int y,const node1 &a,const node1 &b){
    data1[++num].y=y;data1[num].next=h1[x];h1[x]=num;tr0[num]=a;tr1[num]=b;
}
inline int build(int x){
    g[x][0]=g[x][1]=1;visit[x]=1;int vt=0;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (visit[y]) continue;
        int w=build(y);
        if (!w) g[x][0]=(ll)g[x][0]*(g[y][0]+g[y][1])%mod,g[x][1]=(ll)g[x][1]*g[y][0]%mod;
        else{
            if (mark[x]) insert1(x,w,k[y][0]+k[y][1],k[y][0]);
            else k[x][0]=k[y][0]+k[y][1],k[x][1]=k[y][0],vt=w;
        }
    }
    if(mark[x]) k[x][0]=(node1){1,0},k[x][1]=(node1){0,1},vt=x;
    else k[x][0]=k[x][0]*g[x][0],k[x][1]=k[x][1]*g[x][1];
    return vt;
}
inline void dfs(int x){
    dp[x][0]=mk[x][0]?0:g[x][0];dp[x][1]=mk[x][1]?0:g[x][1];
    for (int i=h1[x];i;i=data1[i].next){
        int y=data1[i].y;dfs(y);
        dp[x][0]=(ll)dp[x][0]*((ll)tr0[i].x*dp[y][0]%mod+(ll)tr0[i].y*dp[y][1]%mod)%mod;
        dp[x][1]=(ll)dp[x][1]*((ll)tr1[i].x*dp[y][0]%mod+(ll)tr1[i].y*dp[y][1]%mod)%mod;
    }
}
int main(){
    freopen("bzoj5287.in","r",stdin);
    n=read();m=read();
    for (int i=1;i<=m;++i){
        int x=read(),y=read();
        data[++num].y=y;data[num].next=h[x];h[x]=num;
        data[++num].y=x;data[num].next=h[y];h[y]=num;
    }num=0;dfs_init(1,1);num=0;mark[1]=1;build(1);int S=(1<<cnt)-1;
    for (int i=1;i<=cnt;++i) bin[i]=1<<i-1;int ans=0;
    for (int s=0;s<=S;++s){
        for (int i=1;i<=cnt;++i){
            if (s&bin[i]) mk[fr[i]][0]=1,mk[to[i]][1]=1;
            else mk[fr[i]][1]=1;
        }dfs(1);ans=inc(ans,inc(dp[1][1],dp[1][0]));
        for (int i=1;i<=cnt;++i){
            if (s&bin[i]) mk[fr[i]][0]=0,mk[to[i]][1]=0;
            else mk[fr[i]][1]=0;
        }       
    }printf("%d\n",ans);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值