【JZOJ5626】Map

37 篇文章 0 订阅
4 篇文章 0 订阅

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]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值