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("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);
}