[JZOJ5629]【NOI2018模拟4.4】Map

Description

Rin是个特别好动的少女。
一天Rin来到了一个遥远的都市。这个都市有N个建筑,编号从1到N,其中市中心编号为1,这个都市有M条双向通行的街道,每条街道连接着两个不同的建筑,其中某些街道首尾相连连接成了一个环。Rin通过长时间的走访,已经清楚了这个都市的两个特点:
从市中心出发可以到达所有的建筑物。
任意一条街道最多存在与一个简单环中。令Rin心花怒放的是,每个建筑物都会有拉面售卖。拉面有很多不同的种类,但对于Rin而言只有油腻程度的不同,因此我们把油腻程度相同的拉面看做同一种拉面。由于不同建筑物的拉面的油腻程度可能不同,我们用一个正整数来表示拉面的油腻程度。
要知道,拉面可是Rin的最爱,但是现在到了下班高峰期,都市的交通变得非常的堵塞。Rin只能通过没有被堵死的街道通行,去品尝所在建筑物的拉面。
现在Rin想知道,如果她正在编号为x的建筑物,那么在从市中心到x的所有简单路径经过的街道都被堵死的情况下,Rin可以品尝到的拉面中(注意没有出现的拉面是不能算在里面的):
油腻程度<=y且品尝次数为奇数次的拉面有多少种?
油腻程度<=y且品尝次数为偶数次的拉面有多少种?
n<=100000,询问次数<=100000,边数<=150000,颜色<=1e6

Solution

似乎要各种缩点判环??

并不需要
从1开始DFS,将一个环上深度最小的点设为这个环的环顶,将环上原来的边删掉,环上每个点向环顶连边,其他边不变

那么这就变成一棵树了,可以发现这样变了以后询问就变成一棵树上的子树询问了

离线把询问挂在点上从底向上进行线段树合并即可

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
#define M 650005
using namespace std;
bool bz[M],b2[M];
int fs[N],dep[N],m1,nt[M],dt[M],pr[N],n,m,rt[N],even[50*N],odd[50*N],n1,ask[N][4],st[N][2],top,mx,fx[M],ct[50*N];
vector<int> vis[N];
struct node
{
    int l,r;
}tr[50*N];
void link(int x,int y)
{
    nt[++m1]=fs[x];
    dt[fs[x]=m1]=y;
}
void visit(int k,int f)
{
    int t=top;
    while(t&&st[t][0]!=f) link(f,st[t][0]),link(st[t][0],f),b2[fx[m1]=m1-1]=b2[fx[m1-1]=m1]=1,bz[st[t][1]]=bz[fx[st[t][1]]]=1,t--;
}
void make(int k,int fa,int fn)
{
    dep[k]=dep[fa]+1;
    st[++top][0]=k,st[top][1]=fn;
    for(int i=fs[k];i;i=nt[i])
    {
        int p=dt[i];
        if(b2[i]) continue;
        if(i==fx[fn]) continue;
        if(dep[p]<dep[k]&&dep[p]) visit(k,p),bz[i]=bz[fx[i]]=1;
        else if(!dep[p]) make(p,k,i);
    }
    st[top][0]=0,st[top--][1]=0;
}   
void up(int k)
{
    ct[k]=ct[tr[k].l]+ct[tr[k].r];
    odd[k]=odd[tr[k].l]+odd[tr[k].r];
    even[k]=even[tr[k].l]+even[tr[k].r];
}
void ins(int k,int l,int r,int w)
{
    if(l==r&&l==w)
    {
        ct[k]++;
        odd[k]=(ct[k]&1),even[k]=1-odd[k];
        return;
    }
    int mid=(l+r)/2;
    if(tr[k].l==0) tr[k].l=++n1,tr[k].r=++n1;
    if(w<=mid) ins(tr[k].l,l,mid,w);
    else ins(tr[k].r,mid+1,r,w);
    up(k);
}
int hb(int k,int p,int l,int r)
{
    if(p==0) return k;
    if(k==0) return p;
    if(l==r)
    {
        ct[k]+=ct[p];
        if(ct[k]!=0) odd[k]=(ct[k]&1),even[k]=1-odd[k];
        return k;
    }
    int mid=(l+r)/2;
    tr[k].l=hb(tr[k].l,tr[p].l,l,mid);
    tr[k].r=hb(tr[k].r,tr[p].r,mid+1,r);
    up(k);
}
int get(int k,int l,int r,int x,int y,int v)
{
    x=max(x,l),y=min(y,r);
    if(x>y||!k) return 0;
    if(l==x&&r==y) return(v)?odd[k]:even[k];
    int mid=(l+r)/2;
    return get(tr[k].l,l,mid,x,y,v)+get(tr[k].r,mid+1,r,x,y,v);
}
void dfs(int k,int fa)
{
    rt[k]=++n1;
    for(int i=fs[k];i;i=nt[i])
    {
        int p=dt[i];
        if(p!=fa&&!bz[i]) dfs(p,k),hb(rt[k],rt[p],0,mx);
    }
    ins(rt[k],0,mx,pr[k]);
    for(vector<int>::iterator i=vis[k].begin();i!=vis[k].end();++i)
    {
        ask[*i][3]=get(rt[k],0,mx,0,ask[*i][2],ask[*i][0]);
    }
}
int main()
{
    cin>>n>>m;
    fo(i,1,n) scanf("%d",&pr[i]),mx=max(mx,pr[i]);
    fo(i,1,m)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        link(x,y);
        link(y,x);
        fx[m1]=m1-1,fx[m1-1]=m1;
    }
    make(1,0,0);
    int q;
    cin>>q;
    fo(i,1,q)
    {
        fo(j,0,2) scanf("%d",&ask[i][j]);
        vis[ask[i][1]].push_back(i);
    }
    dfs(1,0);
    fo(i,1,q) printf("%d\n",ask[i][3]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值