bzoj 4631: 踩氣球 並查集+主席樹
Description
六一兒童節到了, SHUXK 被迫陪著M個熊孩子玩一個無聊的遊戲:
有個盒子從左到右排成一排,第個盒子裡裝著個氣球。
SHUXK 要進行次操作,每次從某一個盒子裡拿出一個沒被踩爆的氣球,然後熊孩子們就會立刻把它踩爆。
這個熊孩子每個人都指定了一個盒子區間。 如果某一個時刻,一個熊孩子發現自己選定的盒子區間中的所有氣球都已經被踩爆了,他就會非常高興(顯然之後他一直會很高興)。
為了不辜負將自己的任務強行塞給 SHUXK 的那個人的期望, SHUXK 想向你詢問:
他每次操作過後會有多少個熊孩子很高興。
Input
第一行包含兩個正整數和,分別表示盒子和熊孩子的個數。
第二行包含個正整數,表示每個盒子裡氣球的數量。
以下M行每行包含兩個正整數,分別表示每一個熊孩子指定的區間。
以下一行包含一個正整數,表示 SHUXK 操作的次數。
以下行每行包含一個正整數,表示這次操作是從第個盒子裡拿氣球。為
了體現線上,我們對輸入的進行了加密。
假設輸入的正整數是,那麼真正的。其
中Lastans為上一次詢問的答案。對於第一個詢問, 。
輸入資料保證, 且第X個盒子中有尚未被踩爆的氣球。
Output
包含行,每行輸出一個整數,表示 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]中的氣球已經全部被踩爆)。
分析:
可以暴力修改,只有當某一位減至 時,答案才有可能出現變化。
考慮新增的區間數,顯然只有當這個區間只有這一位不是時才有貢獻。
對於每一個位置,考慮他左邊和右邊的全區間長度,記為和。這個可以使用並查集進行維護,可以通過維護集合大小來算出。
新增的答案就是左邊界在,右邊界在的區間個數。可以給區間按左邊界排序,使用主席樹。每個右邊界權值位置+1,然後求和即可。
複雜度是的。
程式碼:
/**************************************************************
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);
}
}