JZOJ 5629. 【NOI2018模拟4.4】Map

9 篇文章 0 订阅
5 篇文章 0 订阅

Description

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

Input

第一行两个正整数N,M,含义如题所示。
第二行一共N个正整数,第i个数Ai表示第i个建筑物出售的拉面的油腻程度。
接下来M行,每行两个正整数x,y,表示在建筑物x,y之间有一条双向通行的街道。数据保证1<=x,y<=N。
接下来一行一个正整数Q,表示询问个数。
接下来Q行每行三个非负整数ty,x,y,x表示询问的建筑物编号,y表示油腻程度的限制,ty=0时表示询问偶数,ty=1表示询问奇数。

Output

一共q行,对于每个询问输出一个答案。

Sample Input

5 6
2 1 6 7 7
1 2
1 3
2 4
4 5
4 5
1 3
3
0 3 2
0 3 1
0 1 7

Sample Output

0
0
1

Data Constraint

提示:请注意数据范围中的<= ,特殊条件中提到的y均为询问中的y

Data Constraint

Hint

3号建筑物只能到达它自己,而 1号建筑物可以到达所有建筑物。

Solution

  • 看到这种区间的询问问题,就可以考虑莫队算法。

  • 但是首先要把询问节点对应的子树转化成一个个连续的区间。

  • 于是先做一遍正常 tarjan ,再 dfs 一遍,如果一个点 x 连出去是 y,又满足:

    low[y]dfn[x]

  • 则将 y 作为 x 的子树,否则就将 y 作为 low[y] 的子树即可。

  • 这样对 n 个点分块,再对值域也分块,直接莫队即可。

  • 时间复杂度 O(NN)

Code

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cctype>
using namespace std;
const int N=1e5+5;
struct data
{
    int l,r,ty,y,n,id;
}f[N];
int tot,sn,sv;
int first[N],nex[N*3],en[N*3];
int a[N],b[N],ans[N],g[N/100+5][2],cnt[N*10];
int dfn[N],low[N],num[N],id[N],size[N];
bool bz[N];
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline void write(int x)
{
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
inline int max(int x,int y)
{
    return x>y?x:y;
}
inline int min(int x,int y)
{
    return x<y?x:y;
}
inline void insert(int x,int y)
{
    nex[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
}
void tarjan(int x,int y)
{
    num[dfn[x]=low[x]=++tot]=x;
    for(int i=first[x];i;i=nex[i])
        if(en[i]^y)
            if(!dfn[en[i]])
            {
                tarjan(en[i],x);
                low[x]=min(low[x],low[en[i]]);
            }else low[x]=min(low[x],dfn[en[i]]);
}
void dfs(int x)
{
    id[x]=++tot,bz[x]=size[x]=1;
    for(int i=first[x];i;i=nex[i])
        if(!bz[en[i]] && low[en[i]]>=dfn[x])
        {
            dfs(en[i]);
            size[x]+=size[en[i]];
        }
    for(int i=first[x];i;i=nex[i])
        if(!bz[en[i]])
        {
            dfs(en[i]);
            size[num[low[en[i]]]]+=size[en[i]];
        }
}
inline bool cmp(data x,data y)
{
    return x.n<y.n || x.n==y.n && x.r<y.r;
}
inline void update(int x,bool p)
{
    int pos=(b[x]-1)/sv+1;
    if(p)
    {
        if(cnt[b[x]]&1) g[pos][0]++,g[pos][1]--; else
            if(cnt[b[x]]) g[pos][0]--,g[pos][1]++; else g[pos][1]++;
        cnt[b[x]]++;
    }else
    {
        if(cnt[b[x]]==1) g[pos][1]--; else
            if(cnt[b[x]]&1) g[pos][1]--,g[pos][0]++; else g[pos][1]++,g[pos][0]--;
        cnt[b[x]]--;
    }
}
inline int calc(int x,int p)
{
    int sum=0,pos=(x-1)/sv+1;
    for(int i=1;i<pos;i++) sum+=g[i][p];
    for(int i=(pos-1)*sv+1;i<=x;i++)
        if(cnt[i] && (cnt[i]&1)==p) sum++;
    return sum;
}
int main()
{
    int n=read(),m=read();
    for(int i=1;i<=n;i++) sv=max(sv,a[i]=read());
    sn=sqrt(n),sv=sqrt(sv);
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        insert(x,y);
        insert(y,x);
    }
    tarjan(1,tot=0);
    tot=0,dfs(1);
    for(int i=1;i<=n;i++) b[id[i]]=a[i];
    int q=read();
    for(int i=1;i<=q;i++)
    {
        f[i].ty=read();
        int x=read();
        f[i].l=id[x],f[i].r=id[x]+size[x]-1;
        f[i].y=read();
        f[i].id=i,f[i].n=(f[i].l-1)/sn+1;
    }
    sort(f+1,f+1+q,cmp);
    for(int i=1,l=1,r=0;i<=q;i++)
    {
        while(l>f[i].l) update(--l,true);
        while(r<f[i].r) update(++r,true);
        while(l<f[i].l) update(l++,false);
        while(r>f[i].r) update(r--,false);
        ans[f[i].id]=calc(f[i].y,f[i].ty);
    }
    for(int i=1;i<=q;i++) write(ans[i]),putchar('\n');
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值