luogu P4426 [HNOI/AHOI2018]毒瘤
题面传送门
首先题目里这个\(m-n\leq 10\)肯定是有用的。
看到有一个\(m=n-1\)显然直接一个类似没有上司的舞会扔上去:\(f_{i,0/1}\)表示当前点选/不选的方案数,随便转移。
不难想到容斥,算至少\(k\)条非树边不满足的方案数然后乘上容斥系数\((-1)^{|S|}\)
我们\(O(2^k)\)钦定非树边哪些边一定不合法,然后不合法的边两端点就都要选。可以\(O(n)\)做一遍树形dp,可以拿到\(80\)分。
听说极限卡常能过
考虑到每次修改的点很少,啪的一下反手扔了一个动态dp上去就过了。
时间复杂度\(O(nlogn+2^{m-n+4}(m-n+1)log^2n)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define RI re int
#define ll long long
#define db double
#define lb long db
#define N 100000
#define M 300000
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-5)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
using namespace std;
int n,m,k,x,y,H,un,wn,X[15],Y[15],fa[N+5],En[N+5],d[N+5],siz[N+5],Tp[N+5],son[N+5],Id[N+5],Idea,st[205],sh;ll Ans,dp[N+5][2],G[N+5][2];
I ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}
I int GF(int x){return x^fa[x]?fa[x]=GF(fa[x]):x;}I int Count(int x){int cnt=0;while(x) cnt++,x-=x&-x;return cnt;}
struct yyy{int to,z;}tmp;struct ljb{int head,h[N+5];yyy f[N+5<<1];I void add(int x,int y){f[++head]=(yyy){y,h[x]};h[x]=head;}}s,g;
I void dfs1(int x,int La){yyy tmp;d[x]=d[La]+1;siz[x]=1;fa[x]=La;for(int i=s.h[x];i;i=tmp.z) tmp=s.f[i],tmp.to^La&&(dfs1(tmp.to,x),siz[x]+=siz[tmp.to],siz[tmp.to]>siz[son[x]]&&(son[x]=tmp.to));}
I void dfs2(int x,int La){yyy tmp;En[Tp[x]=La]=(Id[x]=++Idea);if(!son[x]) return;dfs2(son[x],La);for(int i=s.h[x];i;i=tmp.z) tmp=s.f[i],tmp.to^fa[x]&&tmp.to^son[x]&&(dfs2(tmp.to,tmp.to),0);}
I void Make(int x,int La){yyy tmp;G[x][0]=G[x][1]=dp[x][0]=dp[x][1]=1;if(!son[x]) return;Make(son[x],x);dp[x][0]=dp[son[x]][0]+dp[son[x]][1];dp[x][1]=dp[son[x]][0];for(int i=s.h[x];i;i=tmp.z) tmp=s.f[i],tmp.to^La&&tmp.to^son[x]&&(Make(tmp.to,x),G[x][0]=G[x][0]*(dp[tmp.to][0]+dp[tmp.to][1])%mod,G[x][1]=G[x][1]*dp[tmp.to][0]%mod);dp[x][0]=dp[x][0]*G[x][0]%mod;dp[x][1]=dp[x][1]*G[x][1]%mod;}
struct MaT{
ll A[2][2];MaT(){Me(A,0);}MaT operator *(const MaT &B)const{
MaT C;RI i,j,h;for(h=0;h<2;h++){
for(i=0;i<2;i++) for(j=0;j<2;j++) C.A[i][j]+=A[i][h]*B.A[h][j];
}for(i=0;i<2;i++) for(j=0;j<2;j++) C.A[i][j]%=mod;return C;
}
}pus,Cl;
namespace Tree{
#define ls now<<1
#define rs now<<1|1
MaT F[N+5<<2];I void Up(int now){F[now]=F[rs]*F[ls];}
I void Get(int x,MaT y,int l=1,int r=n,int now=1){if(l==r) {F[now]=y;return;}int m=l+r>>1;x<=m?Get(x,y,l,m,ls):Get(x,y,m+1,r,rs);Up(now);}
I MaT Find(int x,int y,int l=1,int r=n,int now=1){if(x<=l&&r<=y)return F[now];int m=l+r>>1;MaT C=Cl;y>m&&(C=C*Find(x,y,m+1,r,rs),0);x<=m&&(C=C*Find(x,y,l,m,ls),0);return C;}
}I MaT calc(int x){pus.A[0][0]=pus.A[1][0]=G[x][0];pus.A[1][1]=0;pus.A[0][1]=G[x][1];return pus;}
I void Ins(int x){
int un=x;x=Tp[x];while(fa[x]) pus=Tree::Find(Id[x],En[x]),G[fa[x]][0]=G[fa[x]][0]*mpow(pus.A[0][0]+pus.A[0][1])%mod,G[fa[x]][1]=G[fa[x]][1]*mpow(pus.A[0][0])%mod,x=Tp[fa[x]];x=un;G[x][0]=0;
Tree::Get(Id[x],calc(x));st[++sh]=x;x=Tp[x];while(fa[x]) pus=Tree::Find(Id[x],En[x]),G[fa[x]][0]=G[fa[x]][0]*(pus.A[0][0]+pus.A[0][1])%mod,G[fa[x]][1]=G[fa[x]][1]*pus.A[0][0]%mod,Tree::Get(Id[fa[x]],calc(fa[x])),st[++sh]=fa[x],x=Tp[fa[x]];
}
int main(){
freopen("1.in","r",stdin);
RI i,j,h;Cl.A[0][0]=Cl.A[1][1]=1;scanf("%d%d",&n,&m);for(i=1;i<=n;i++) fa[i]=i;for(i=1;i<=m;i++) scanf("%d%d",&x,&y),un=GF(x),wn=GF(y),un^wn?(s.add(x,y),s.add(y,x),fa[un]=wn):(X[++H]=x,Y[H]=y);
dfs1(1,0);dfs2(1,1);Make(1,0);memcpy(dp,G,sizeof(G));for(i=1;i<=n;i++) Tree::Get(Id[i],calc(i));
for(i=0;i<(1<<H);i++){
for(j=0;j<H;j++) i>>j&1&&(Ins(X[j+1]),Ins(Y[j+1]),0);pus=Tree::Find(1,En[1]);
Ans+=Count(i)&1?mod-pus.A[0][0]-pus.A[0][1]:pus.A[0][0]+pus.A[0][1];while(sh) G[st[sh]][0]=dp[st[sh]][0],G[st[sh]][1]=dp[st[sh]][1],Tree::Get(Id[st[sh]],calc(st[sh])),sh--;//printf("%d %lld\n",i,(Ans%mod+mod)%mod);
}printf("%lld\n",(Ans%mod+mod)%mod);
}