1. 程式人生 > >bzoj 4631: 踩氣球 並查集+主席樹

bzoj 4631: 踩氣球 並查集+主席樹

Description
六一兒童節到了, SHUXK 被迫陪著M個熊孩子玩一個無聊的遊戲:
NN個盒子從左到右排成一排,第ii個盒子裡裝著AiA_i個氣球。
SHUXK 要進行QQ次操作,每次從某一個盒子裡拿出一個沒被踩爆的氣球,然後熊孩子們就會立刻把它踩爆。
MM個熊孩子每個人都指定了一個盒子區間[Li,Ri][L_i, R_i]。 如果某一個時刻,一個熊孩子發現自己選定的盒子區間[Li,Ri][L_i, R_i]中的所有氣球都已經被踩爆了,他就會非常高興(顯然之後他一直會很高興)。
為了不辜負將自己的任務強行塞給 SHUXK 的那個人的期望, SHUXK 想向你詢問:
他每次操作過後會有多少個熊孩子很高興。
Input

第一行包含兩個正整數NNMM,分別表示盒子和熊孩子的個數。
第二行包含NN個正整數Ai1<=Ai<=105A_i( 1 < = A_i < = 10^5),表示每個盒子裡氣球的數量。
以下M行每行包含兩個正整數Li,Ri1<=Li<=Ri<=NL_i, R_i( 1 < = L_i < = R_i < = N),分別表示每一個熊孩子指定的區間。
以下一行包含一個正整數Q

Q,表示 SHUXK 操作的次數。
以下QQ行每行包含一個正整數XX,表示這次操作是從第XX個盒子裡拿氣球。為
了體現線上,我們對輸入的XX進行了加密。
假設輸入的正整數是xx',那麼真正的X=(x+Lastans1)mod  N+1X = (x' + Lastans − 1)\mod N + 1。其
中Lastans為上一次詢問的答案。對於第一個詢問, Lastans=0Lastans = 0
輸入資料保證1<
=x<=1091 < = x' < = 10^9
, 且第X個盒子中有尚未被踩爆的氣球。
N<=105,M<=105,Q<=105N < = 10^5 ,M < = 10^5 ,Q < = 10^5

Output

包含QQ行,每行輸出一個整數,表示 SHUXK 一次操作後詢問的
答案。答案的順序應與輸入資料的順序保持一致。
Sample Input
5 3
1 1 1 1 1
5 5
2 2
1 3
5
4
2
5
2
3

Sample Output
0
1
1
2
3

【樣例說明】
實際上每次操作的盒子是: 4 2 1 3 5
在第二次操作後,第二個熊孩子會高興 (區間[2,2]中的氣球已經全部被踩爆)。
在第四次操作後,第三個熊孩子會高興(區間[1,3]中的氣球已經全部被踩爆)。
在第五次操作後,第一個熊孩子會高興(區間[5,5]中的氣球已經全部被踩爆)。

分析:
可以暴力修改,只有當某一位減至 00 時,答案才有可能出現變化。
考慮新增的區間數,顯然只有當這個區間只有這一位不是00時才有貢獻。
對於每一個位置xx,考慮他左邊和右邊的全00區間長度,記為LLRR。這個可以使用並查集進行維護,可以通過維護集合大小來算出。
新增的答案就是左邊界在[xL,x][x-L,x],右邊界在[x,x+R][x,x+R]的區間個數。可以給區間按左邊界排序,使用主席樹。每個右邊界權值位置+1,然後求和即可。
複雜度是O(nlogn)O(nlogn)的。

程式碼:

/**************************************************************
    Problem: 4631
    User: ypxrain
    Language: C++
    Result: Accepted
    Time:1416 ms
    Memory:27464 kb
****************************************************************/
 
#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>
 
const int maxn=1e5+7;
 
using namespace std;
 
int n,m,T,x,ans,cnt;
int a[maxn],root[maxn],num[maxn],p[maxn],val[maxn];
 
struct rec{
    int l,r;
}e[maxn];
 
struct node{
    int l,r,data;
}t[maxn*20];
 
void ins(int &p,int q,int l,int r,int x,int k)
{
    if (!p) p=++cnt;
    t[p].data=t[q].data+1;
    if (l==r) return;
    int mid=(l+r)/2;
    if (x<=mid) t[p].r=t[q].r,ins(t[p].l,t[q].l,l,mid,x,k);
           else t[p].l=t[q].l,ins(t[p].r,t[q].r,mid+1,r,x,k);
          
}
 
int getsum(int p,int q,int l,int r,int x,int y)
{
    if ((l==x) && (r==y)) return t[p].data-t[q].data;
    int mid=(l+r)/2;
    if (y<=mid) return getsum(t[p].l,t[q].l,l,mid,x,y);
    else if (x>mid) return getsum(t[p].r,t[q].r,mid+1,r,x,y);
    else return getsum(t[p].l,t[q].l,l,mid,x,mid)+getsum(t[p].r,t[q].r,mid+1,r,mid+1,y);
}
 
int find(int x)
{
    if (!p[x]) return x;
    return p[x]=find(p[x]);
}
 
void uni(int x,int y)
{
    int u=find(x),v=find(y);
    if (u==v) return;
    p[u]=v;
    val[v]+=val[u];
}
 
bool cmp(rec a,rec b)
{
    return a.l<b.l;
}
 
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=m;i++) scanf("%d%d",&e[i].l,&e[i].r);
    sort(e+1,e+m+1,cmp);
    int k=1;
    for (int i=1;i<=n;i++)
    {
        while (e[k].l==i)
        {
            ins(root[k],root[k-1],1,n,e[k].r,1);
            k++;
        }
        num[i]=k-1;
    }
    scanf("%d",&T);
    int l,r;        
    for (int i=1;i<=T;i++)
    {
        scanf("%d",&x);
        x=(x+ans-1)%n+1;
        a[x]--;
        if (!a[x])
        {
            val[x]++;
            l=val[find(x-1)],r=val[find(x+1)];
            if ((x>1) && (!a[x-1])) uni(x-1,x);
            if ((x<n) && (!a[x+1])) uni(x,x+1);
            ans+=getsum(root[num[x]],root[num[x-l-1]],1,n,x,x+r);
        }
        printf("%d\n",ans);
    }
}