BZOJ4564 [Haoi2016]地图

6 篇文章 0 订阅
5 篇文章 0 订阅

仙人掌转成序列,然后莫队

仙人掌转成序列的方法是先tarjan找环,对于一个环上的点,如果是环根,那么这个环都属于其子仙人掌,如果不是环根,那么除了他自己这个环上都不是其子仙人掌,这样再dfs一次,对于一个点,优先走其出边里是其子仙人掌的部分,同时对于一个环,把不与环根相连的点的siz加到环根的siz里,这样仙人掌就转成了序列,一个子仙人掌对应的区间就是dfn[x]~dfn[x]+siz[x]-1

之后直接上莫队,用数据结构维护的话是n sqrtn logn的,T到爆,用和GTY的二逼妹子序列相同的方法,对权值分块,维护每块和每个点对答案的贡献,修改是单点修改,这样修改其所在块和这个位置,询问的时候变成sqrtn即可

数据好水啊,我在第二次dfs判这条出边和x是不是在一个环的时候没用tarjan的dfn而用了新dfs序都过了-_-

人生中第一次在BZOJ首A-_-

update:NOI拍照现场看到松爷,吓得我赶紧复习一发仙人掌,结果发现之前的代码有点问题……不过这题数据太弱了,写成啥JB样都能过

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
using namespace std;
#define MAXN 100010
#define MAXM 300010
#define MAXC 1000010
#define ll long long
#define INF 1000000000
#define MOD 1000000007
#define eps 1e-8
struct vec{
	int to;
	int fro;
};
struct que{
	int cl;
	int v;
	int l;
	int r;
	int k;
	int num;
	friend bool operator <(que x,que y){
		return x.k!=y.k?x.k<y.k:x.r<y.r;
	}
};
int A[MAXN],a[MAXN];
int c;
que q[MAXN];
vec mp[MAXM];
int tai[MAXN],cnt=1;
int dfn[MAXN],DFN[MAXN],low[MAXN],ndf[MAXN],tim;
int n,m;
int siz[MAXN];
int ANS[2][MAXC];
int CNT[MAXC];
int Asiz;
int ans[MAXN];
inline void be(int x,int y){
	mp[++cnt].to=y;
	mp[cnt].fro=tai[x];
	tai[x]=cnt;
}
inline void bde(int x,int y){
	be(x,y);
	be(y,x);
}
void dfs(int x,int f){
	int i,y;
	DFN[x]=low[x]=++tim;
	ndf[tim]=x;
	for(i=tai[x];i;i=mp[i].fro){
		y=mp[i].to;
		if(i==(f^1)){
			continue ;
		}
		if(!DFN[y]){
			dfs(y,i);
			low[x]=min(low[x],low[y]);
		}else{
			low[x]=min(low[x],DFN[y]);
		}
	}
}
void dfs2(int x,int f){
    int i,y;
    siz[x]=1;
    dfn[x]=++tim;
    for(i=tai[x];i;i=mp[i].fro){
        y=mp[i].to;
        if(i==(f^1)){
            continue ;
        }
        if(!dfn[y]&&low[y]>=DFN[x]){
            dfs2(y,i);
            if(low[y]>DFN[x]){
                siz[x]+=siz[y];
            }
        }
    }
    for(i=tai[x];i;i=mp[i].fro){
        y=mp[i].to;
        if(i==(f^1)){
            continue ;
        }
        if(!dfn[y]&&low[y]<DFN[x]){
            dfs2(y,i);
        }
    }
    if(low[x]!=DFN[x]){
        siz[ndf[low[x]]]+=siz[x];
    }
}
int main(){
	int i,j,o,x,y;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++){
		scanf("%d",&A[i]);
		c=max(c,A[i]);
	}
	Asiz=sqrt(c);
	for(i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		bde(x,y);
	}
	dfs(1,0);
	tim=0;
	dfs2(1,0);
	for(i=1;i<=n;i++){
		a[dfn[i]]=A[i];
	}
	scanf("%d",&m);
	int SIZ=sqrt(n);
	for(i=1;i<=m;i++){
		scanf("%d%d%d",&q[i].cl,&q[i].l,&q[i].v);
		q[i].r=dfn[q[i].l]+siz[q[i].l]-1;
		q[i].l=dfn[q[i].l];
		q[i].k=q[i].l/SIZ;
		q[i].num=i;
	}
	sort(q+1,q+m+1);
	int L=1,R=0;
	for(i=1;i<=m;i++){
		while(L>q[i].l){
			L--;
			x=(a[L]-1)/Asiz+1;
			if(CNT[a[L]]){
				ANS[CNT[a[L]]&1][x]--;
			}
			CNT[a[L]]++;
			ANS[CNT[a[L]]&1][x]++;
		}
		while(R<q[i].r){
			R++;
			x=(a[R]-1)/Asiz+1;
			if(CNT[a[R]]){
				ANS[CNT[a[R]]&1][x]--;
			}
			CNT[a[R]]++;
			ANS[CNT[a[R]]&1][x]++;
		}
		while(L<q[i].l){
			x=(a[L]-1)/Asiz+1;
			ANS[CNT[a[L]]&1][x]--;
			CNT[a[L]]--;
			if(CNT[a[L]]){
				ANS[CNT[a[L]]&1][x]++;
			}
			L++;
		}
		while(R>q[i].r){
			x=(a[R]-1)/Asiz+1;
			ANS[CNT[a[R]]&1][x]--;
			CNT[a[R]]--;
			if(CNT[a[R]]){
				ANS[CNT[a[R]]&1][x]++;
			}
			R--;
		}
		x=(q[i].v-1)/Asiz+1;
		for(j=1;j<x;j++){
			ans[q[i].num]+=ANS[q[i].cl][j];
		}
		for(j=(x-1)*Asiz+1;j<=q[i].v;j++){
			if(CNT[j]&&((CNT[j]&1)==q[i].cl)){
				ans[q[i].num]++;
			}
		}
	}
	for(i=1;i<=m;i++){
		printf("%d\n",ans[i]);
	}
	return 0;
}

/*
7 8
1 2
2 3
3 4
4 1
3 5
5 6
6 7
7 3

5 6
2 1 6 7 7
1 2
1 3
2 4
4 5
4 5
1 3
3
0 3 2
0 3 1
0 1 7

*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值