• P3180[HAOI2016]地图【圆方树,莫队,分块】


    正题

    题目链接:https://www.luogu.com.cn/problem/P3180


    题目大意

    \(n\)个点\(m\)条边的一个仙人掌,有点权。

    \(Q\)次询问给出\(op,x,y\),封闭\(1\)\(x\)号点的所有简单路径后\(x\)能到达的点的点权中,小于\(y\)且出现次数为奇数/偶数的权值数目。

    \(1\leq n,Q\leq 10^5,1\leq m\leq 1.5\times 10^5,0\leq y,w_i\leq 10^6\)


    解题思路

    梦魇融合怪是吧

    先对仙人掌建立一个圆方树,以\(1\)为根,那么能到达的就变为了子树的点权了。然后转换到\(dfs\)序的区间询问问题。

    后面那个就和P4867-Gty的二逼妹子序列差不多了,考虑莫队,然后平衡结合的话用分块来维护区间和就好了。

    时间复杂度\(O(n\sqrt y)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<stack>
    #include<cmath>
    using namespace std;
    const int N=1e6+10,M=1100;
    struct node{
    	int l,r,lim,k,id;
    }q[N];
    int n,m,Q,T,dfc,dfn[N],low[N],rfn[N],pos[N],c[N];
    int w[N],ans[N],L[M],R[M],v[2][N],sum[2][M];
    vector<int> G[N],H[N];stack<int> s;
    void tarjan(int x){
    	dfn[x]=low[x]=++dfc;s.push(x);
    	for(int i=0;i<G[x].size();i++){
    		int y=G[x][i];
    		if(!dfn[y]){
    			tarjan(y); 
    			low[x]=min(low[x],low[y]);
    			if(low[y]==dfn[x]){
    				++n;int k;
    				do{
    					k=s.top();s.pop();
    					H[n].push_back(k);
    					H[k].push_back(n);
    				}while(k!=y);
    				H[n].push_back(x);
    				H[x].push_back(n);
    			}
    		}
    		else low[x]=min(low[x],dfn[y]);
    	}
    	return;
    }
    void dfs(int x,int fa){
    	dfn[++dfc]=x;rfn[x]=dfc;
    	for(int i=0;i<H[x].size();i++){
    		int y=H[x][i];
    		if(y==fa)continue;
    		dfs(y,x);
    	}
    	low[x]=dfc;
    }
    bool cmp(node x,node y){
    	if(x.l/T==y.l/T)return x.r<y.r;
    	return x.l<y.l; 
    }
    void Add(int x,int f){
    	if(c[x])v[c[x]&1][x]--,sum[c[x]&1][pos[x]]--;
    	c[x]+=f;
    	if(c[x])v[c[x]&1][x]++,sum[c[x]&1][pos[x]]++;
    	return;
    }
    int Ask(int k,int l,int r){
    	if(!r)return 0;
    	int x=pos[l],y=pos[r],ans=0;
    	if(x==y){
    		for(int i=l;i<=r;i++)
    			ans+=v[k][i];
    		return ans;
    	}
    	for(int i=l;i<=R[x];i++)ans+=v[k][i];
    	for(int i=L[y];i<=r;i++)ans+=v[k][i];
    	for(int i=x+1;i<y;i++)ans+=sum[k][i];
    	return ans;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    		scanf("%d",&w[i]);
    	for(int i=1;i<=m;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		G[x].push_back(y);
    		G[y].push_back(x);
    	}
    	tarjan(1);dfc=0;
    	dfs(1,1);T=sqrt(1e6);
    	for(int i=1;i<=T;i++)L[i]=R[i-1]+1,R[i]=i*T;
    	if(R[T]!=1e6)++T,L[T]=R[T-1]+1,R[T]=n;
    	for(int i=1;i<=T;i++)
    		for(int j=L[i];j<=R[i];j++)pos[j]=i;
    	scanf("%d",&Q);
    	for(int i=1;i<=Q;i++){
    		int x;
    		scanf("%d%d%d",&q[i].k,&x,&q[i].lim);
    		q[i].l=rfn[x];q[i].r=low[x];q[i].id=i;
    	}
    	sort(q+1,q+1+Q,cmp);
    	int l=1,r=0;
    	for(int i=1;i<=Q;i++){
    		while(l<q[i].l)Add(w[dfn[l]],-1),l++;
    		while(l>q[i].l)l--,Add(w[dfn[l]],1);
    		while(r<q[i].r)r++,Add(w[dfn[r]],1);
    		while(r>q[i].r)Add(w[dfn[r]],-1),r--;
    		ans[q[i].id]=Ask(q[i].k,1,q[i].lim);
    	}
    	for(int i=1;i<=Q;i++)
    		printf("%d\n",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    HDU 3401 Trade
    POJ 1151 Atlantis
    HDU 3415 Max Sum of MaxKsubsequence
    HDU 4234 Moving Points
    HDU 4258 Covered Walkway
    HDU 4391 Paint The Wall
    HDU 1199 Color the Ball
    HDU 4374 One hundred layer
    HDU 3507 Print Article
    GCC特性之__init修饰解析 kasalyn的专栏 博客频道 CSDN.NET
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14578687.html
Copyright © 2020-2023  润新知