Description
Solution
给出的图是一个仙人掌,那么有经典做法。
考虑从询问入手,如果选择一个点,那么其点所在的简单环(且与根较近的)到根都不能贡献。那么对于每个简单环,我们拆掉所有的边,改为每个点向它的环顶(在此环内与根连通)连边。
那么询问变为子树问题,可以放到序列上用莫队,或直接线段树合并即可。
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
#define rep(i,x) for(int i=ls[x];i;i=nx[i])
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
const int N=1e5+10,M=3e5+10;
int a[N];
int to[M],nx[M],ls[N],num=1;
int an[N];
void link(int u,int v){
to[++num]=v,nx[num]=ls[u],ls[u]=num;
}
bool bz[M],vis[N];
vector<int> c[N];//link
struct node{
int y,t;
}q[N];
vector<int> d[N];//ask
struct tree{
int l,r,t0,t1;
}tr[N*60];
int rt[N];
int tot=0,mx=0;//segtree merge
int co[N],hd[N],col=0;//color
int dfn[N];
int st[N],top=0;
void linkc(int u,int v){
c[u].push_back(v),c[v].push_back(u);
}
void pre(int x){
dfn[x]=++top,st[top]=x;
rep(i,x) if(!bz[i]){
bz[i]=bz[i^1]=1;
int v=to[i];
if(!dfn[v]) pre(v);
else if(dfn[v]<dfn[x]){
col++;
for(int j=top;j && st[j]!=v;j--)
co[st[j]]=col,hd[st[j]]=v,linkc(v,st[j]);
}
}
top--;
}
void pre2(int x){
vis[x]=1;
rep(i,x) if(!bz[i]){
int v=to[i];
if(vis[v]) continue;
bz[i]=bz[i^1]=1;
if((co[x]!=co[v] || !co[x] && !co[v]) && hd[v]!=x && hd[x]!=v) linkc(x,v);
pre2(v);
}
}
void update(int v){
tr[v].t0=tr[tr[v].l].t0+tr[tr[v].r].t0;
tr[v].t1=tr[tr[v].l].t1+tr[tr[v].r].t1;
}
void merge(int v,int v1,int l,int r){
if(l==r){
if(!tr[v].t0 && !tr[v].t1) tr[v]=tr[v1];
else if(tr[v1].t0 || tr[v1].t1){
if(tr[v].t1==tr[v1].t1) tr[v].t1=0,tr[v].t0=1;
else tr[v].t1=1,tr[v].t0=0;
}
return;
}
int mid=(l+r)>>1;
if(!tr[v].l) tr[v].l=tr[v1].l;
else if(tr[v1].l) merge(tr[v].l,tr[v1].l,l,mid);
if(!tr[v].r) tr[v].r=tr[v1].r;
else if(tr[v1].r) merge(tr[v].r,tr[v1].r,mid+1,r);
update(v);
}
int insert(int v,int l,int r,int x){
if(!v) v=++tot;
if(l==r){
if(tr[v].t1) tr[v].t1=0,tr[v].t0=1;
else tr[v].t1=1,tr[v].t0=0;
return v;
}
int mid=(l+r)>>1;
x<=mid?tr[v].l=insert(tr[v].l,l,mid,x):tr[v].r=insert(tr[v].r,mid+1,r,x);
update(v);
return v;
}
int find(int v,int l,int r,int x,int y,int t){
if(!v) return 0;
if(l==x && r==y) return t?tr[v].t1:tr[v].t0;
int mid=(l+r)>>1;
if(y<=mid) return find(tr[v].l,l,mid,x,y,t);
else if(x>mid) return find(tr[v].r,mid+1,r,x,y,t);
else return find(tr[v].l,l,mid,x,mid,t)+find(tr[v].r,mid+1,r,mid+1,y,t);
}
void dfs(int x,int fr){
int o=c[x].size();
rt[x]=++tot;
fo(i,0,o-1){
int v=c[x][i];
if(v==fr) continue;
dfs(v,x);
merge(rt[x],rt[v],0,mx);
}
rt[x]=insert(rt[x],0,mx,a[x]);
o=d[x].size();
fo(i,0,o-1){
int p=d[x][i];
an[p]=find(rt[x],0,mx,0,q[p].y,q[p].t);
}
}
int main()
{
freopen("map.in","r",stdin);
freopen("map.out","w",stdout);
int n,m;
scanf("%d %d",&n,&m);
fo(i,1,n) scanf("%d",&a[i]),mx=max(mx,a[i]);
fo(i,1,m){
int u,v;
scanf("%d %d",&u,&v);
link(u,v),link(v,u);
}
pre(1);
mem(bz),pre2(1);
int Q;
scanf("%d",&Q);
fo(i,1,Q){
int x;
scanf("%d %d %d\n",&q[i].t,&x,&q[i].y);
mx=max(mx,q[i].y);
d[x].push_back(i);
}
dfs(1,0);
fo(i,1,Q) printf("%d\n",an[i]);
}