luogu P3180 [HAOI2016]地图 仙人掌 线段树合并 圆方树

LINK:地图

考虑如果是一棵树怎么做 权值可以离散 那么可以直接利用dsu on tree+树状数组解决。

当然 也可以使用莫队 不过前缀和比较难以维护 外面套个树状数组又带了个log 套分块然后就可以了。

最暴力的当然是线段树合并了。

此时考虑这是个仙人掌 仔细画图 发现一些比较好的性质 某个点x 除了自己某个儿子的low比自己的dfn小 剩下的都大。

剩下的显然都可以造成贡献 利用dfn和low可以轻松判断 然后上线段树合并即可。

省空间的话可以直接离线 当然也可以在线做。

不过这个做法不是常规的 仙人掌一般和圆方树联系在一起。

直接从仙人掌想到构造圆方树 然后 那么就转换成上面的问题了。

一个细节:非负整数 包括0 别犯sb错误。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<ctime>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<algorithm>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<bitset>
#include<list>
#include<map>
#include<set>
#include<utility>
#include<iomanip>
#define RE register
#define ll long long
#define putl(x) printf("%lld\n",x)
#define gt(x) scanf("%d",&x)
#define put(x) printf("%d\n",x)
#define get(x) x=read()
#define rep(p,n,i) for(int i=p;i<=n;++i)
#define vep(p,n,i) for(int i=p;i<n;++i)
#define fep(n,p,i) for(int i=n;i>=p;--i)
#define pii pair<int,int>
#define mod 998244353
#define sum0(i) t[i].sum0
#define sum1(i) t[i].sum1
#define pb push_back
#define l(i) t[i].l
#define r(i) t[i].r
#define mk make_pair
#define S second
#define F first
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
	RE int x=0,f=1;RE char ch=getc();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
	return x*f;
}
const int MAXN=100010,maxn=150010;
int n,m,Q,top,cnt,len,maxx,id,ans0,ans1,ss;
int dfn[MAXN],low[MAXN],root[MAXN],s[MAXN],q[MAXN],ans[MAXN];
int lin[MAXN],ver[maxn<<1],nex[maxn<<1],a[MAXN];
vector<pii>g[MAXN];
struct wy
{
	int l,r;
	int sum1,sum0;
}t[MAXN*21];
inline void add(int x,int y)
{
	ver[++len]=y;
	nex[len]=lin[x];
	lin[x]=len;
}
inline void modify(int &p,int l,int r,int x)
{
	if(!p)p=++id;
	if(l==r)return sum1(p)=1,void();
	int mid=(l+r)>>1;
	if(x<=mid)modify(l(p),l,mid,x);
	else modify(r(p),mid+1,r,x);
	sum1(p)=sum1(l(p))+sum1(r(p));
	sum0(p)=sum0(l(p))+sum0(r(p));
}
inline int merge(int x,int y,int l,int r)
{
	if(!x||!y)return x|y;
	if(l==r)
	{
		if(sum0(x)&&sum0(y))return x;
		if(sum0(x)&&sum1(y))return y;
		if(sum1(x)&&sum0(y))return x;
		sum0(x)=1;sum1(x)=0;
		return x;
	}
	int mid=(l+r)>>1;
	l(x)=merge(l(x),l(y),l,mid);
	r(x)=merge(r(x),r(y),mid+1,r);
	sum1(x)=sum1(l(x))+sum1(r(x));
	sum0(x)=sum0(l(x))+sum0(r(x));
	return x;
}
inline void ask(int p,int l,int r,int L,int R)
{
	if(L<=l&&R>=r)
	{
		ans0+=sum0(p);
		ans1+=sum1(p);
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid)ask(l(p),l,mid,L,R);
	if(R>mid)ask(r(p),mid+1,r,L,R);
}
inline void dfs(int x)
{
	s[++top]=x;dfn[x]=low[x]=++cnt;
	modify(root[x],0,maxx,a[x]);
	for(int i=lin[x];i;i=nex[i])
	{
		int tn=ver[i];
		if(!dfn[tn])
		{
			dfs(tn);
			low[x]=min(low[x],low[tn]);
			if(low[tn]>=dfn[x])
			{
				int y=s[top];ss=0;
				while(y!=x)
				{
					q[++ss]=y;
					y=s[--top];
				}
				rep(1,ss,i)root[x]=merge(root[x],root[q[i]],0,maxx);
			}
		}
		else low[x]=min(low[x],dfn[tn]);
	}
	vep(0,g[x].size(),i)
	{
		ans0=ans1=0;
		ask(root[x],0,maxx,0,min(maxx,ans[g[x][i].S]));
		if(g[x][i].F==0)ans[g[x][i].S]=ans0;
		else ans[g[x][i].S]=ans1;
	}
}
int main()
{
	//freopen("1.in","r",stdin);
	get(n);get(m);
	rep(1,n,i)get(a[i]),maxx=max(maxx,a[i]);
	rep(1,m,i)
	{
		int get(x),get(y);
		add(x,y);add(y,x);
	}
	get(Q);
	rep(1,Q,i)
	{
		int get(op);
		int get(x),get(y);
		g[x].pb(mk(op,i));
		ans[i]=y;
	}
	dfs(1);
	rep(1,Q,i)put(ans[i]);
	return 0;
}
posted @ 2020-06-18 19:42  chdy  阅读(217)  评论(0编辑  收藏  举报