3180 [HAOI2016]地图(tarjan)(dfs序)(分块)(莫队)

4 篇文章 0 订阅
2 篇文章 0 订阅

题目

洛谷3180 HAOI2016地图

题解

tarjan+dfs序+分块+莫队
首先用tarjan求出所有的环根,做个dfs,就可以统计出每个节点在不挑剔的情况下的值了。其实我并不懂怎么是怎么实现的最后可以做到x~x+son[x]-1(dfs序)都是在封路后可以到达的节点。
接下来用dfs序,可以把树给展开,方便统计子树的情况。
再接着就用莫队来控制区间,用分块来求出小于一定值的点有多少个。
最后输出答案。

整体思路是这样的,细节上要注意我们一共分了两次块,一次在莫队,一次是真的分块。
真的分块求的时候按油腻程度分成几大块,块外暴力求和,块内逐一判断。

代码

不要抄代码,要不你会后悔一辈子的!

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=100010,MAXM=150010,MAXQ=100010,MAXA=1000010;

inline void read(int &x)
{
    x=0;char ch=getchar();
    while(ch<'0' || ch>'9') ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+(ch^48),ch=getchar();
}
inline void write(int x)
{
    if(x>=10) write(x/10);
    putchar(x%10|48);
}

int n,m,Q,sz1,sz2;
int o[MAXN],bl[MAXN];
struct Que{int l,r,x,opt,id;}q[MAXQ];

bool cmp(Que q1,Que q2)
{
    if(bl[q1.l]!=bl[q2.l]) return bl[q1.l]<bl[q2.l];
    return q1.r<q2.r;//debug bl[q1.r]<bl[q2.r];
}

struct E{int y,next;}e[MAXM*2];int len=1,last[MAXN];
void ins(int x,int y)
{
    e[++len]=(E){y,last[x]};last[x]=len;
}

int dfs_colck=0,dfn[MAXN],low[MAXN],ys[MAXN];
void tarjan(int x,int fa)
{
    dfn[x]=low[x]=++dfs_colck;
    ys[dfs_colck]=x;
    for(int k=last[x];k;k=e[k].next)
    {
        int y=e[k].y;
        if(y==fa) continue;
        if(!dfn[y]) tarjan(y,x),low[x]=min(low[x],low[y]);
        else low[x]=min(low[x],dfn[y]);
    }
}

bool vis[MAXN];
int tot=0,id[MAXN],son[MAXN];
void dfs(int x)
{
    vis[x]=true;
    id[x]=++tot;
    son[x]=1;
    for(int k=last[x];k;k=e[k].next)
    {
        int y=e[k].y;
        if(vis[y] || dfn[x]>low[y]) continue;//不在环中 debug dfn[x]>low[y]
        dfs(y);
        son[x]+=son[y];
    }
    for(int k=last[x];k;k=e[k].next)
    {
        int y=e[k].y;
        if(vis[y]) continue;//if(dfn[x]<=low[y]) 在环上 
        dfs(y);
        son[ys[low[y]]]+=son[y];//debug son[ys[low[x]]]+=low[y];
    }
}

int oil[MAXN];
int sum[2][MAXN],cnt[MAXA];
void update(int x,bool flag)
{
    int val=oil[x],p=(val-1)/sz2+1;//debug p=(x-1)/sz2+1
    if(flag)
    {
        if(!cnt[val]) sum[1][p]++;
        else if(cnt[val]&1) sum[1][p]--,sum[0][p]++;
        else sum[0][p]--,sum[1][p]++;
        cnt[val]++;
    }
    else
    {
        if(cnt[val]==1) sum[1][p]--;
        else if(cnt[val]&1) sum[1][p]--,sum[0][p]++;
        else sum[0][p]--,sum[1][p]++;
        cnt[val]--;
    }
}
int calc(int x,int opt)
{
    int re=0,p=(x-1)/sz2+1;
    for(int i=1;i<p;i++) re+=sum[opt][i];
    for(int i=(p-1)*sz2+1;i<=x;i++) re+=cnt[i]&&(cnt[i]&1)==opt;
    return re;
}

int ans[MAXQ];
int main()
{
    read(n),read(m);
    sz1=sqrt(n);
    for(int i=1;i<=sz1;i++) bl[i]=(i-1)/sz1+1;
    for(int i=1;i<=n;i++) read(o[i]),sz2=max(sz2,o[i]);
    sz2=sqrt(sz2);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        read(x),read(y);
        ins(x,y);ins(y,x);
    }
    
    tarjan(1,0);
    dfs(1);
    for(int i=1;i<=n;i++) oil[id[i]]=o[i];
    
    read(Q);
    for(int i=1;i<=Q;i++)
    {
        int x;
        read(q[i].opt),read(x),read(q[i].x);
        q[i].l=id[x];q[i].r=id[x]+son[x]-1;
        q[i].id=i;
    }
    sort(q+1,q+Q+1,cmp);
    
    int l=1,r=0;
    for(int i=1;i<=Q;i++)
    {
        while(q[i].l<l) update(--l,1);//增 
        while(q[i].l>l) update(l++,0);//减 
        while(q[i].r<r) update(r--,0);//减 
        while(q[i].r>r) update(++r,1);//增 
        ans[q[i].id]=calc(q[i].x,q[i].opt);
    }
    
    for(int i=1;i<=Q;i++) write(ans[i]),putchar('\n');
    return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值