• 【BZOJ3351】[IOI2009] regions(设阈值+分块)


    点此看题面

    大致题意: 一棵树上每个节点有一个颜色,每次询问给出两种颜色(x,y),问有多少对颜色分别为(x,y)的点是祖先与后代的关系((x)为祖先,(y)为后代)。

    前言

    从没想过经过闪指导的指导后还会再一次死在分块上。。。

    一开始偷懒了点不想写离线,结果复杂度是(O(nsqrt nlogn)),然后就(TLE)了。

    原以为常数问题,花了一个晚上+半个上午去调参+卡常,结果最多只能把原本的(87)分升到(94)分。

    只好老老实实写了个离线,不过复杂度还是(O(nsqrt nlogn)),于是就变成了(97)分。。。

    最终,只能不再偷懒,把树状数组换成了分块(其实也挺好写的),才过了此题。

    设阈值

    首先,我们把树上问题通过(dfs)序转化为序列问题(一个子树对应一个区间,这我相信大家都知道)。

    对于这道题,我们显然可以设阈值:

    • 对于小于等于(sqrt n)的情况,我们枚举第一种颜色,然后枚举这种颜色相关的询问统计答案。利用分块(O(sqrt n))区间修改、(O(1))单点求值的复杂度,可以实现(O(nsqrt n))的修改和(O(msqrt n))的求值。
    • 对于大于(sqrt n)的情况,因为最多只有(sqrt n)种这样的颜色,故可以直接枚举进行打表。每次差分+前缀和修改,然后枚举所有颜色直接求值,单次操作都是(O(n))的,因此总复杂度(O(nsqrt n))

    代码

    //代码中有些地方因为先前的智障写法,可能会为了卡常、卡内存而显得有点奇怪,不过懒得改了。。。
    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 200000
    #define M 25000
    #define BOUND 300
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    #define pb push_back
    using namespace std;
    int n,m,a[N+5],cnt[N+5],ee,lnk[N+5],d,dI[N+5],dO[N+5],ans[N+5];
    struct edge {int to,nxt;}e[N];vector<int> V[N+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C==E&&(clear(),0),*C++=c)
    		#define tn (x<<3)+(x<<1)
    		#define D isdigit(c=tc())
    		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
    		Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
    		Tp I void writeln(Con Ty& x) {write(x),pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    }F;
    class LargeIniter//对于大数据打表
    {
    	private:
    		int s[N+5],g[N+5],tmp[N+5];
    	public:
    		int p[N+5];vector<int> s1[M+5],s2[M+5];
    		I void Solve()
    		{
    			for(RI i=1,x,tot=0;i<=m;++i)
    			{
    				if(cnt[i]<BOUND) continue;for(p[i]=tot++,x=1;x<=n;++x) s[x]=0;
    				for(x=1;x<=n;++x) s[dI[x]]+=a[x]==i;for(x=1;x<=n;++x) g[x]=g[x-1]+s[x];//差分+前缀和
    				for(x=1;x<=n;++x) tmp[a[x]]+=g[dO[x]]-g[dI[x]];//求值
    				for(x=1;x<=m;++x) s1[x].pb(tmp[x]),tmp[x]=0;
    				for(x=1;x<=n;++x) s[dO[x]+1]-=a[x]==i;for(x=1;x<=n;++x) g[x]=g[x-1]+s[x];//差分+前缀和
    				for(x=1;x<=n;++x) tmp[a[x]]+=g[dI[x]];//求值
    				for(x=1;x<=m;++x) s2[x].pb(tmp[x]),tmp[x]=0;
    			}
    		}
    }P;
    class LittleSolver//对于小数据离线搞
    {
    	private:
    		int sz,bl[N+5],S[N+5],BV[N+5];
    		I void BF(CI x,CI y,CI v) {for(RI i=x;i<=y;++i) S[i]+=v;}//散块暴力
    		I void U(CI x,CI y,CI v)
    		{
    			if(bl[x]==bl[y]) return BF(x,y,v);BF(x,bl[x]*sz,v),BF((bl[y]-1)*sz+1,y,v);
    			for(RI i=bl[x]+1;i^bl[y];++i) BV[i]+=v;//整块打标记
    		}
    	public:
    		struct data {int id,k;I data(CI x=0,CI y=0):id(x),k(y){}};vector<data> q[M+5];
    		vector<int>::iterator it;vector<data>::iterator Q;
    		I void Solve()
    		{
    			RI i,t;for(sz=sqrt(n),i=1;i<=n;++i) bl[i]=(i-1)/sz+1;//预处理
    			for(i=1;i<=m;++i) if(!q[i].empty())
    			{
    				for(it=V[i].begin();it!=V[i].end();++it) U(dI[*it],dO[*it],1);//修改
    				for(Q=q[i].begin();Q!=q[i].end();++Q)//枚举询问
    				{
    					for(t=0,it=V[Q->k].begin();it!=V[Q->k].end();++it) t+=S[dI[*it]]+BV[bl[dI[*it]]];//统计答案
    					ans[Q->id]=t;
    				}
    				for(it=V[i].begin();it!=V[i].end();++it) U(dI[*it],dO[*it],-1);//清空
    			}
    		}
    }S;
    I void Init(CI x=1) {dI[x]=++d;for(RI i=lnk[x];i;i=e[i].nxt) Init(e[i].to);dO[x]=d;}//求子树对应区间
    int main()
    {
    	RI Qt,i,x,y;F.read(n),F.read(m),F.read(Qt),F.read(a[1]),cnt[a[1]]=1;
    	for(i=2;i<=n;++i) F.read(x),F.read(a[i]),add(x,i),++cnt[a[i]];//注意统计每种颜色点的个数
    	for(i=1;i<=n;++i) cnt[a[i]]<BOUND&&(V[a[i]].pb(i),0);Init(),P.Solve();
    	for(i=1;i<=Qt;++i) if(F.read(x),F.read(y),cnt[y]>=BOUND) ans[i]=P.s1[x][P.p[y]];
    		else if(cnt[x]>=BOUND) ans[i]=P.s2[y][P.p[x]];else S.q[x].pb(LittleSolver::data(i,y));
    	for(S.Solve(),i=1;i<=Qt;++i) F.writeln(ans[i]);return F.clear(),0;
    }
    
  • 相关阅读:
    AttributeError: 'DatasetV1Adapter' object has no attribute 'group_by_window'版本兼容问题解决
    Jupyter kernel管理
    AttributeError: module 'tensorflow_core._api.v2.nn' has no attribute 'rnn_cell' tensorflow版本兼容问题-解决
    在虚拟机Linux中安装VMTools遇到的问题-小结
    VuGen总结
    Controller控制器
    事物
    loadrunner参数化
    编写get和post请求
    loadrunner组成
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ3351.html
Copyright © 2020-2023  润新知