「AHOI / HNOI2018」毒瘤 (DDP)(链分治)

L O J LOJ LOJ 传送门

  • 题解:首先考虑一棵树怎么做,就是树形 d p dp dp,然后我们可以枚举每一条边怎么选,显然有 3 种情况
    进一步发现只需要枚举两种,即强制 u u u v v v 不选,和强制 u u u 不选 v v v 随意
    那么现在的问题就是每次 b a n ban ban 掉一些点选一些点不选,动态更新根节点的 d p dp dp
    然后把转移写成矩阵的形式链分治,由于可以除 0 0 0 所以手写了一个用 x ∗ 0 y x*0^y x0y 表示每个数的类
    复杂度 O ( n + k ∗ 2 k ∗ l o g ( n ) 2 ) O(n+k*2^k*log(n)^2) O(n+k2klog(n)2) 被虚树吊打
#include<bits/stdc++.h>
#define pb push_back
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
cs int N = 1e5 + 50, K = 15;
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int ksm(int a, int b){ int ans=1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a); return ans; }
void Add(int &a, int b){ a = add(a, b); }
void Mul(int &a, int b){ a = mul(a, b); }
int n, m, k, u[K], v[K];
vector<int> G[N];
int sz[N], son[N], fa[N], dep[N], in[N], top[N], bot[N], sgn;
struct mat{
	int a[2][2];
	mat(int _x=0, int _y=0){ a[0][0]=a[1][0]=_x; a[0][1]=_y; a[1][1]=0; }
	mat operator * (cs mat &A){
		mat B; 
		for(int i=0; i<2; i++) for(int k=0; k<2; k++) if(a[i][k])
		for(int j=0; j<2; j++) if(A.a[k][j]) Add(B.a[i][j],mul(a[i][k],A.a[k][j]));
		return B;
	}
	int vl(){ return add(a[0][0],a[0][1]); }
};
struct num{
	int x,y;
	num(){ x = y = 1; }
	void operator = (int v){ v ? (x=v,y=0) : (x=y=1); }
	void operator *= (int v){ v ? x=mul(x,v) : ++y; }
	void operator /= (int v){ v ? x=mul(x,ksm(v,Mod-2)) : --y; }
	operator int()cs{ return y?0:x; }
};
void pre_dfs(int u, int f){
	sz[u] = 1;
	for(int v : G[u]) if(v ^ f){
		fa[v] = u; dep[v] = dep[u]+1; pre_dfs(v,u);
		sz[u] += sz[v]; if(sz[son[u]]<sz[v]) son[u]=v;
	}
}
namespace SGT{
	cs int N = ::N<<2;
	mat vl[N];
	#define mid ((l+r)>>1)
	void pushup(int x){ vl[x] = vl[x<<1|1] * vl[x<<1]; }
	void modify(int x, int l, int r, int p, mat v){
		if(l == r){ vl[x] = v; return; }
		(p<=mid)?modify(x<<1,l,mid,p,v):modify(x<<1|1,mid+1,r,p,v); pushup(x);
	}
	mat query(int x, int l, int r, int L, int R){
		if(L<=l&&r<=R) return vl[x]; 
		if(R<=mid) return query(x<<1,l,mid,L,R);
		else if(L>mid) return query(x<<1|1,mid+1,r,L,R);
		else return query(x<<1|1,mid+1,r,L,R)*query(x<<1,l,mid,L,R);
	}
}
int f[N][2]; num coe[N][2];
void dfs(int u, int tp){
	top[u] = tp; in[u]=++sgn;
	if(son[u]) dfs(son[u],tp), bot[u]=bot[son[u]];
	else bot[u]=u; 
	coe[u][0]=1; coe[u][1]=1;
	for(int v : G[u]) if(v!=fa[u]&&v!=son[u])
		dfs(v,v), coe[u][0]*=add(f[v][0],f[v][1]), coe[u][1]*=f[v][0];
	SGT::modify(1,1,n,in[u],mat(coe[u][0],coe[u][1]));
	if(son[u]) f[u][0]=mul(coe[u][0],add(f[son[u]][0],f[son[u]][1])), f[u][1]=mul(coe[u][1],f[son[u]][0]);
	else f[u][0]=f[u][1]=1;
}
int state[N];
void upt(int u){
	mat now;
	while(u){
		int tp = fa[top[u]];
		if(tp){
			now = SGT::query(1,1,n,in[top[u]],in[bot[u]]);
			coe[tp][0]/=now.vl(), coe[tp][1]/=now.a[0][0];
		}
		if(state[u]==1) SGT::modify(1,1,n,in[u],mat(coe[u][0],0));
		else if(state[u]==2) SGT::modify(1,1,n,in[u],mat(0,coe[u][1]));
		else SGT::modify(1,1,n,in[u],mat(coe[u][0],coe[u][1]));
		if(tp){
			now = SGT::query(1,1,n,in[top[u]],in[bot[u]]);
			coe[tp][0]*=now.vl(), coe[tp][1]*=now.a[0][0];
		} u = tp;
	}
}
int anc[N]; 
int find(int x){ return x==anc[x]?x:anc[x]=find(anc[x]); }
bool ck(int S){
	for(int i=0; i<k; i++) state[u[i]]=state[v[i]]=0;
	for(int i=0; i<k; i++){
		if(S>>i&1){
			if(state[u[i]]==1) return false; state[u[i]]=2;
			if(state[v[i]]==2) return false; state[v[i]]=1;
		}
		else{
			if(state[u[i]]==2) return false; state[u[i]]=1;
		}
	} return true;
}
int main(){
	n = read(), m = read();
	for(int i=1; i<=n; i++) anc[i]=i;
	for(int i=1,x,y; i<=m; i++){
		x=read(), y=read();
		if(find(x)^find(y)) G[x].pb(y),G[y].pb(x),anc[find(x)]=find(y);
		else u[k]=x,v[k++]=y;
	}
	pre_dfs(1,0); dfs(1,1); int as = 0;
	for(int S=0; S<(1<<k); S++){
		if(!ck(S)) continue;
		for(int i=0; i<k; i++){
			if(S>>i&1) upt(u[i]), upt(v[i]);
			else upt(u[i]);
		}
		Add(as,SGT::query(1,1,n,in[1],in[bot[1]]).vl());
		for(int i=0; i<k; i++) state[u[i]]=state[v[i]]=0;
		for(int i=k-1; ~i; i--){
			if(S>>i&1) upt(v[i]), upt(u[i]); 
			else upt(u[i]); 
		}
	} cout << as; return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值