LOJ#3340.【NOI2020】命运(destiny)

Description

LOJ3340
在这里插入图片描述

  • n , m < = 5 e 5 n,m<=5e5 n,m<=5e5

Solution

  • 首先很容易想到 n 2 n^2 n2的DP, f [ x ] [ j ] f[x][j] f[x][j]表示 x x x点的限制到 j j j
  • 把有用的状态提出来就可以用 n 2 n^2 n2获得64分。
  • 启发式合并可以做到 n l o g 2 n nlog^2n nlog2n获得更多分数。
  • 很容易就可以在DP的基础上想到线段树合并。
  • 但是我考场上的时候想的是前缀和的状态的合并,需要单点乘以及区间加,并且将一段赋为0。由于这个需要维护一个 k x + b kx+b kx+b的tag,还需要打区间赋值的tag,我就没有实现出来(???)。现在想想属实弱智。
  • 首先区间赋值的tag可以变成*0,也可以把变成0的节点直接删掉,但是还是要维护 k x + b kx+b kx+b的tag的下传和合并(虽然也不难是吧)。
  • 实际上我也想过直接用正常的状态转移,但是没有想到前缀和怎么合并。。。实际上只需要在线段树合并的时候维护一个前缀和就好了,这样就只需要维护一个区间乘的tag了。how stupid i am!
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 500005
#define ll long long 
#define mo 998244353
#define maxm 20000005
using namespace std;

int n,m,i,j,k,dep[maxn],q[maxn];
int em,e[maxn*2],nx[maxn*2],ls[maxn];

void read(int &x){
    
    
	x=0; char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar());
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
}

void insert(int x,int y){
    
    
	em++; e[em]=y; nx[em]=ls[x]; ls[x]=em;
	em++; e[em]=x; nx[em]=ls[y]; ls[y]=em;
}

int tot,M,tl[maxm],tr[maxm],rt[maxn];
ll t[maxm],tag[maxm],s1,s2;
void dfs(int x,int p){
    
    
	dep[x]=dep[p]+1,M=max(M,dep[x]);
	for(int i=ls[x];i;i=nx[i]) if (e[i]!=p)
		dfs(e[i],x);
}

int newnode(){
    
    tot++,t[tot]=0,tag[tot]=1;return tot;}
void downtag(int x){
    
    
	t[x]=t[x]*tag[x]%mo;
	if (tl[x]) tag[tl[x]]=tag[tl[x]]*tag[x]%mo;
	if (tr[x]) tag[tr[x]]=tag[tr[x]]*tag[x]%mo;
	tag[x]=1;
}

void add(int &x,int l,int r,int p,ll d){
    
    
	if (!x) x=newnode(); if (tag[x]!=1) downtag(x); 
	(t[x]+=d)%=mo; if (l==r) return; 
	int mid=(l+r)>>1;
	if (p<=mid) add(tl[x],l,mid,p,d);
	else add(tr[x],mid+1,r,p,d);
}

void change(int x,int l,int r,int L,int R,ll v){
    
    
	if (x&&tag[x]!=1) downtag(x);
	if (!x||l>R||r<L) return;
	if (L<=l&&r<=R) {
    
    tag[x]=tag[x]*v%mo;downtag(x);return;}
	int mid=(l+r)>>1;
	change(tl[x],l,mid,L,R,v);
	change(tr[x],mid+1,r,L,R,v);
	t[x]=(t[tl[x]]+t[tr[x]])%mo;
}

ll find(int x,int l,int r,int L,int R){
    
    
	if (!x||l>R||r<L) return 0;
	if (tag[x]!=1) downtag(x);
	if (L<=l&&r<=R) return t[x];
	int mid=(l+r)>>1;
	return find(tl[x],l,mid,L,R)+find(tr[x],mid+1,r,L,R);
}

void merge(int x,int y,int l,int r){
    
    
	if (tag[x]!=1) downtag(x); if (tag[y]!=1) downtag(y);
	if (l==r) {
    
    (s1+=t[x])%=mo,(s2+=t[y])%=mo,t[x]=(t[x]*s2+t[y]*s1-t[x]*t[y])%mo;return;}
	int mid=(l+r)>>1;
	if (tl[x]&&tl[y]) merge(tl[x],tl[y],l,mid); else{
    
    
		if (tl[x]&&!tl[y]) (s1+=t[tl[x]]*tag[tl[x]])%=mo,tag[tl[x]]=tag[tl[x]]*s2%mo; else 
		if (!tl[x]&&tl[y]) (s2+=t[tl[y]]*tag[tl[y]])%=mo,tag[tl[y]]=tag[tl[y]]*s1%mo,tl[x]=tl[y];
	}
	if (tr[x]&&tr[y]) merge(tr[x],tr[y],mid+1,r); else{
    
    
		if (tr[x]&&!tr[y]) (s1+=t[tr[x]]*tag[tr[x]])%=mo,tag[tr[x]]=tag[tr[x]]*s2%mo; else 
		if (!tr[x]&&tr[y]) (s2+=t[tr[y]]*tag[tr[y]])%=mo,tag[tr[y]]=tag[tr[y]]*s1%mo,tr[x]=tr[y];
	}
	t[x]=(t[tl[x]]*tag[tl[x]]+t[tr[x]]*tag[tr[x]])%mo;
}

void dfs2(int x,int p){
    
    
	add(rt[x],0,M,0,1);
	for(int i=ls[x];i;i=nx[i]) if (e[i]!=p){
    
    
		dfs2(e[i],x);
		s1=s2=0,merge(rt[x],rt[e[i]],0,M);
	}
	if (q[x]) {
    
    
		add(rt[x],0,M,q[x],find(rt[x],0,M,0,q[x]-1));
		change(rt[x],0,M,0,q[x]-1,0);
	}
	if (x>1){
    
    
		add(rt[x],0,M,0,t[rt[x]]*tag[rt[x]]%mo);
		change(rt[x],0,M,dep[x]-1,dep[x]-1,0);
	}
}

int main(){
    
    
//	freopen("ceshi.in","r",stdin);
	freopen("destiny.in","r",stdin);
	freopen("destiny.out","w",stdout);
	read(n);
	for(i=1;i<n;i++) read(j),read(k),insert(j,k);
	dfs(1,0),read(m);
	for(i=1;i<=m;i++) read(j),read(k),q[k]=max(q[k],dep[j]);
	dfs2(1,0);
	printf("%lld\n",(t[rt[1]]*tag[rt[1]]%mo+mo)%mo);
}

猜你喜欢

转载自blog.csdn.net/qq_43649416/article/details/108392425