Description
Rin是个特别好动的少女。
一天Rin来到了一个遥远的都市。这个都市有N个建筑,编号从1到N,其中市中心编号为1,这个都市有M条双向通行的街道,每条街道连接着两个不同的建筑,其中某些街道首尾相连连接成了一个环。Rin通过长时间的走访,已经清楚了这个都市的两个特点:
从市中心出发可以到达所有的建筑物。
任意一条街道最多存在与一个简单环中。令Rin心花怒放的是,每个建筑物都会有拉面售卖。拉面有很多不同的种类,但对于Rin而言只有油腻程度的不同,因此我们把油腻程度相同的拉面看做同一种拉面。由于不同建筑物的拉面的油腻程度可能不同,我们用一个正整数来表示拉面的油腻程度。
要知道,拉面可是Rin的最爱,但是现在到了下班高峰期,都市的交通变得非常的堵塞。Rin只能通过没有被堵死的街道通行,去品尝所在建筑物的拉面。
现在Rin想知道,如果她正在编号为x的建筑物,那么在从市中心到x的所有简单路径经过的街道都被堵死的情况下,Rin可以品尝到的拉面中(注意没有出现的拉面是不能算在里面的):
油腻程度<=y且品尝次数为奇数次的拉面有多少种?
油腻程度<=y且品尝次数为偶数次的拉面有多少种?
n,m 1e5
???
看了个把小时并没有发现这是仙人掌。
先将一个环中,从1开始最先走到的的设为环顶(由仙人掌的定义显然唯一,1所在的环设1为根就行)。缩成仙人掌树
LL的高级缩法:求出low与dfn,按原顺序dfs,对于一条边(a,b),若dfn[a]<=low[b],那么a是一个割点,可以将下面的点fa设为到a.
若dfn[a]>low[b],那么b会有一条向上的边。 此时a,b应在同一个环中,所以fa[b]=fa[a]。
可以发现,查询x点的答案,就是x子树的答案。
考虑线段树合并。从下往上做,搞完这个点的询问就把它合并到父亲那里去。
合并的时间复杂度与两颗线段树公共点成线性。
但据说总时间是nlogn的,证明不详
不过观察代码可以发现。
我们不妨设想为将所有儿子都合并到第一个儿子上,当x,y都不为空时,就要继续往下做。 这时候y这个点相当于被废弃。而初始总点数不超过nlogn,因此可以发现往下做的次数不超过nlogn.因此可以认为时间复杂度就是O(nlogn) (可能会带较大常数,至少是2吧)
void merge(int &x,int y,int l,int r) {
if (y==0) return;
if (x==0) x=y; else {
if (l==r) {
int cs=T[x][1]+T[y][1];
T[x][0]=T[x][1]=0; T[x][cs&1]=1;
return;
}
merge(c[x][0],c[y][0],l,l+r>>1);
merge(c[x][1],c[y][1],(l+r>>1)+1,r);
update(x);
}
}
因此复杂度为
O(nlogn)
O
(
n
l
o
g
n
)
这一题就开权值线段树存一下,种类x的奇贡献,偶贡献。 合并时求和,注意叶子特判即可。
Code
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int M=1.5e5+10,N=1e5+10;
int to[M*2],nex[M*2],final[N],tot,d[N];
int qy[N][2],qnex[N],qfinal[N],loc[N],qtot,ans[N];
int fa[N];
int n,m,q,a[N];
void link(int x,int y) {
to[++tot]=y,nex[tot]=final[x],final[x]=tot;
}
int low[N],dfn[N],stm;
void form(int x) {
low[x]=dfn[x]=++stm;
for (int i=final[x];i;i=nex[i]) {
int y=to[i]; if (!dfn[y]) {
form(y);
low[x]=min(low[x],low[y]);
} else low[x]=min(low[x],dfn[y]);
}
}
void build(int x) {
for (int i=final[x]; i; i=nex[i]) {
int y=to[i]; if (dfn[y]>dfn[x] && fa[y]==0) {
if (dfn[x]<=low[y]) fa[y]=x,d[x]++; else fa[y]=fa[x],d[fa[x]]++;
build(y);
}
}
}
int qu,Q[N],L,R,T[2700000][2],c[2700000][2],fft,root[N],mx;
void update(int x) {
T[x][0]=T[c[x][0]][0]+T[c[x][1]][0];
T[x][1]=T[c[x][0]][1]+T[c[x][1]][1];
}
void query(int x,int l,int r,int qw,int qloc,int &sum) {
if (!x || l>qloc) return;
if (r<=qloc) {
sum+=T[x][qw];
return;
}
query(c[x][0],l,l+r>>1,qw,qloc,sum);
query(c[x][1],(l+r>>1)+1,r,qw,qloc,sum);
}
void merge(int &x,int y,int l,int r) {
if (y==0) return;
if (x==0) x=y; else {
if (l==r) {
int cs=T[x][1]+T[y][1];
T[x][0]=T[x][1]=0; T[x][cs&1]=1;
return;
}
merge(c[x][0],c[y][0],l,l+r>>1);
merge(c[x][1],c[y][1],(l+r>>1)+1,r);
update(x);
}
}
void change(int &x,int l,int r,int tg) {
if (x==0) x=++fft;
if (l==r) {
if (T[x][0]+T[x][1]==0) T[x][1]=1;
else T[x][0]^=1,T[x][1]^=1;
return;
}
if (tg<=(l+r>>1)) change(c[x][0],l,l+r>>1,tg);
else change(c[x][1],(l+r>>1)+1,r,tg);
update(x);
}
int main() {
freopen("map.in","r",stdin);
freopen("map.out","w",stdout);
cin>>n>>m;
for (int i=1; i<=n; i++) scanf("%d",&a[i]),mx=max(a[i],mx);
for (int i=1; i<=m; i++) {
int x,y; scanf("%d %d",&x,&y); link(x,y),link(y,x);
}
cin>>qu;
for (int i=1; i<=qu; i++) {
int ty,x,y; scanf("%d %d %d",&ty,&x,&y);
qy[++qtot][0]=ty,qy[qtot][1]=y;
qnex[qtot]=qfinal[x]; qfinal[x]=qtot; loc[qtot]=i;
}
form(1);
build(1);
for (int i=1; i<=n; i++) if (d[i]==0) Q[++R]=i;
while (L<R) {
int x=Q[++L];
change(root[x],1,mx,a[x]);
for (int i=qfinal[x];i;i=qnex[i]) {
query(root[x],1,mx,qy[i][0],qy[i][1],ans[loc[i]]);
}
if (fa[x]) merge(root[fa[x]],root[x],1,mx);
if (x && --d[fa[x]]==0) Q[++R]=fa[x];
}
for (int i=1; i<=qu; i++) printf("%d\n",ans[i]);
}