[洛谷P3180] HAOI2016 地图
问题描述
一天rin来到了一个遥远的都市。这个都市有n个建筑,编号从1到n,其中市中心编号为1,这个都市有m条双向通行的街道,每条街道连接着两个建筑,其中某些街道首尾相连连接成了一个环。
rin通过长时间的走访,已经清楚了这个都市的两个特点:1. 从市中心出发可以到达所有的建筑物。2. 任意一条街道最多存在与一个简单环中。令rin心花怒放的是,每个建筑物都会有拉面售卖。拉面有很多不同的种类,但对于rin而言只有油腻程度的不同,因此我们把油腻程度相同的拉面看做同一种拉面。
由于不同建筑物的拉面的油腻程度可能不同,我们用一个正整数来表示拉面的油腻程度。
要知道,拉面可是rin的最爱,但是现在到了下班高峰期,都市的交通变得非常的堵塞。 rin只能通过没有被堵死的街道通行,去品尝所在建筑物的拉面。
现在rin想知道,如果她正在编号为x的建筑物,那么在从市中心到x的所有简单路径经过的街道都被堵死的情况下,rin可以品尝到的拉面中(注意没有出现的拉面是不能算在里面的):1. 油腻程度<= y且品尝次数为奇数次的拉面有多少种?2. 油腻程度<= y且品尝次数为偶数次的拉面有多少种?
输入格式
第一行两个正整数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表示询问奇数。N<=100000,M<=150000,Q<=100000,Ai<=106提示:请注意数据范围中的<=,特殊条件中提到的??均为询问中的y,对于所有的数据,有y<=106
输出格式
一共Q行,对于每个询问输出一个答案。
样例输入
10 12
1 10 4 5 2 10 1 8 4 8
1 2
1 3
1 4
2 5
4 6
4 7
7 8
2 9
8 10
1 6
8 10
4 7
10
0 3 9
1 7 6
0 5 2
1 10 9
0 5 7
1 7 4
0 7 3
1 2 7
0 3 4
0 3 8
样例输出
0
1
0
1
0
1
0
2
0
0
解析
不难看出这是一个仙人掌,首先建出圆方树。观察到按要求删边之后一个点能够到达的点都在圆方树上这个点的子树中。我们把这颗子树用dfn序表示出来就变成了一个区间。那么现在问题转化为求一个区间中小于等于 \(y\) 并且出现次数为奇数/偶数的数量。
我们用莫队解决这个问题,但不好 \(O(1)\) 地更新状态。我们考虑用值域分块维护这个东西,在块中记录下出现奇数次的个数和出现偶数次的个数即可。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <algorithm>
#define N 200002
#define M 200002
using namespace std;
struct query{
int op,l,r,y,id;
}a[N];
vector<int> v[N];
int head[N*2],ver[M*2],nxt[M*2],l;
int n,m,q,i,j,tim,dfn[N],low[N],top,s[N],cnt,c[N],in[N],out[N],pos[N],val[N];
int b[N],gap,maxx,ans[N],num[N],sum[N][2];
int read()
{
char c=getchar();
int w=0;
while(c<'0'||c>'9') c=getchar();
while(c<='9'&&c>='0'){
w=w*10+c-'0';
c=getchar();
}
return w;
}
void insert(int x,int y)
{
l++;
ver[l]=y;
nxt[l]=head[x];
head[x]=l;
}
void Tarjan(int x,int root)
{
dfn[x]=low[x]=++tim;
s[++top]=x;
if(x==root&&!head[x]){
v[++cnt].push_back(x);
top--;
return;
}
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(!dfn[y]){
Tarjan(y,root);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){
cnt++;
while(1){
v[cnt].push_back(s[top]);
top--;
if(y==s[top+1]) break;
}
v[cnt].push_back(x);
}
}
else low[x]=min(low[x],dfn[y]);
}
}
void dfs(int x,int pre)
{
if(x<=n) in[x]=++tim,pos[tim]=x;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y!=pre) dfs(y,x);
}
if(x<=n) out[x]=tim;
}
int my_comp(const query &x,const query &y)
{
if(b[x.l]==b[y.l]) return x.r<y.r;
return x.l<y.l;
}
void add(int x)
{
if(num[x]%2!=0) sum[b[x]][1]--,sum[b[x]][0]++;
else{
sum[b[x]][1]++;
if(num[x]!=0) sum[b[x]][0]--;
}
num[x]++;
}
void del(int x)
{
num[x]--;
if(num[x]%2!=0) sum[b[x]][1]++,sum[b[x]][0]--;
else{
sum[b[x]][1]--;
if(num[x]!=0) sum[b[x]][0]++;
}
}
int ask(int x,int op)
{
int ans=0;
for(int i=1;i<b[x];i++) ans+=sum[i][op];
for(int i=(b[x]-1)*gap+1;i<=x;i++){
if(num[i]%2==op&&num[i]!=0) ans++;
}
return ans;
}
int main()
{
n=read();m=read();
for(i=1;i<=n;i++){
c[i]=read();
val[++maxx]=c[i];
}
for(i=1;i<=m;i++){
int u=read(),v=read();
insert(u,v);insert(v,u);
}
Tarjan(1,1);
memset(head,0,sizeof(head));l=tim=0;
for(i=1;i<=cnt;i++){
for(j=0;j<v[i].size();j++) insert(n+i,v[i][j]),insert(v[i][j],n+i);
}
dfs(1,0);
q=read();
for(i=1;i<=q;i++){
int op=read(),x=read(),y=read();
a[i]=(query){op,in[x],out[x],y,i};
val[++maxx]=y;
}
sort(val+1,val+maxx+1);
maxx=unique(val+1,val+maxx+1)-val-1;
for(i=1;i<=n;i++) c[i]=lower_bound(val+1,val+maxx+1,c[i])-val;
for(i=1;i<=q;i++) a[i].y=lower_bound(val+1,val+maxx+1,a[i].y)-val;
gap=sqrt(1.0*q);
for(i=1;i<=q;i++) b[i]=(i-1)/gap+1;
sort(a+1,a+q+1,my_comp);
gap=sqrt(1.0*maxx);
for(i=1;i<=maxx;i++) b[i]=(i-1)/gap+1;
int l=1,r=0;
for(i=1;i<=q;i++){
while(r<a[i].r) add(c[pos[++r]]);
while(l<a[i].l) del(c[pos[l++]]);
while(l>a[i].l) add(c[pos[--l]]);
while(r>a[i].r) del(c[pos[r--]]);
ans[a[i].id]=ask(a[i].y,a[i].op);
}
for(i=1;i<=q;i++) printf("%d\n",ans[i]);
return 0;
}