zoukankan      html  css  js  c++  java
  • Noip前的大抱佛脚----数据结构

    数据结构

    Tags:Noip前的大抱佛脚

    知识点及其应用

    线段树

    注意:空间开4倍

    神奇标记

    From8.26 Test_zsy(CPU监控)
    如果一个点权为(val)的点被打上了((a,b))标记,那么他的实际点权为(max(a+val,b))
    干啥滴?

    标记不下放

    • 区间加标记不下放,维护区间max或者最大值
      方法是当前(tag)维护当前区域标记,(t)维护左右儿子的(max+tag[now]),并没有快多少,如果仍然忘记见提交记录

    并查集

    维护二分图

    并查集每个点维护是否要改颜色,然后按秩合并/按大小合并即可
    实际上可以说是用期望树高为(logn)的树形结构维护信息

    维护后继位置

    快速得到后继位置

    • [BZOJ2054]疯狂的馒头 维护数列的后继位置

      对一个数列进行(10^7)次区间覆盖,查询最终颜色。

      倒序做,每次做完一个区间把([l,r])的并查集父亲指向(r+1),表示扫到该区间,要从(r+1)开始染色,这样就可以快速得到下一个染色的位置,复杂度为(O(n))

    • [BZOJ2238]Mst 维护树形结构的后继位置

      对树上路径进行染色,查询最终颜色。

      路径拆成直上直下的两条,(x,lca、y,lca),于是每次弄完把([x,lca])的并查集父亲指向(fa[lca]),就可以类似数列一样快速求得下一个位置了,复杂度为(O(n))

    对于所有的父子结构,一定都有父亲的优先级大于左右儿子的优先级

    可并堆的可持久化

    左偏树可持久化,每次合并两个堆,只需要给经过的(log)个结点复制一遍就好了

    #define lc H[x].ch[0]
    #define rc H[x].ch[1]
    struct heap {int ch[2],dis;double w;}H[M];
    int Merge(int x,int y)
    {
        if(!x||!y) return x+y;
        H[++node]=H[x];x=node;
        if(H[x].w>H[y].w) swap(x,y);
        rc=Merge(rc,y);
        if(H[lc].dis<H[rc].dis) swap(lc,rc);
        H[x].dis=H[rc].dis+1;
        return x;
    }
    

    这个可以用来完成(k)短路问题

    dsu on tree

    姑且叫它数据结构

    方式&原理

    步骤:

    • 先递归做轻儿子的答案,再递归做重儿子的答案
    • 暴力把当前结点的轻儿子扫一遍统计答案
    • 如果当前结点是递归下来的轻儿子,扫一遍把答案清空,否则不做处理

    要求: 离线算法

    复杂度: (O(nlogn))

    分析:只需要考虑每个点被扫的次数,树剖下来每个点到根的路径最多有(log)条链,每逢轻重链交替的时候就会暴力去扫,于是每个点最多只会被扫(log)次,复杂度得证

    适用范围

    适用一些子树信息不好统计的题,可以类比于树上莫队

    • 统计子树内数量最多的颜色的编号之和(Codeforces600E)
    • 统计子树内距离(i)点不超过(k)的点的颜色数/某权值之和(BZOJ4771七彩树离线版/[湖南集训]谈笑风生)

    单调队列

    尺取合法区间

    这大概是一个常见用法。
    丢一道萝卜的题:长度为(n)的序列,每个数是([l[i],r[i]])内的一个值,求可能的LIS长度。

    解法:当新加入的区间右端点大于前面所有区间的左端点时,产生矛盾。维护一个左端点的下降队列即可。

    模板库

    线段树

    【模板】线段树 2
    就是标记的优先顺序

    const int N=1e5+10;
    int n,m,t[N<<2],t1[N<<2],t2[N<<2],P;
    void mul(int &x,int y) {x=1ll*x*y%P;}
    void put(int x,int k,int op,int l,int r)
    {
    	if(op==1) (t[x]+=1ll*(r-l+1)*k%P)%=P,(t2[x]+=k)%=P;
    	else mul(t[x],k),mul(t2[x],k),mul(t1[x],k);
    }
    void pushdown(int x,int l,int mid,int r)
    {
    	int &s1=t1[x],&s2=t2[x];
    	if(s1!=1) put(x<<1,s1,2,l,mid),put(x<<1|1,s1,2,mid+1,r),s1=1;
    	if(s2!=0) put(x<<1,s2,1,l,mid),put(x<<1|1,s2,1,mid+1,r),s2=0;
    }
    void Add(int now,int l,int r,int gl,int gr,int op,int k)
    {
    	if(l>=gl&&r<=gr) {put(now,k,op,l,r);return;}
    	int mid=(l+r)>>1;
    	pushdown(now,l,mid,r);
    	if(gl<=mid) Add(now<<1,l,mid,gl,gr,op,k);
    	if(gr>mid) Add(now<<1|1,mid+1,r,gl,gr,op,k);
    	t[now]=(t[now<<1]+t[now<<1|1])%P;
    }
    int Query(int now,int l,int r,int gl,int gr)
    {
    	if(l>=gl&&r<=gr) return t[now];
    	int mid=(l+r)>>1,res=0;
    	pushdown(now,l,mid,r);
    	if(gl<=mid) res=Query(now<<1,l,mid,gl,gr);
    	if(gr>mid) res+=Query(now<<1|1,mid+1,r,gl,gr);
    	return res%P;
    }
    int main()
    {
    	cin>>n>>m>>P;
    	for(int i=1;i<=n*4;i++) t1[i]=1;
    	for(int i=1,x;i<=n;i++) cin>>x,Add(1,1,n,i,i,1,x);
    	for(int i=1,x,y,k,op;i<=m;i++)
    	{
    		cin>>op>>x>>y;
    		if(op==1) cin>>k,Add(1,1,n,x,y,2,k);
    		if(op==2) cin>>k,Add(1,1,n,x,y,1,k);
    		if(op==3) cout<<Query(1,1,n,x,y)<<endl;
    	}
    	return 0;
    }
    

    点分治

    一种分治思想,解决树上链的问题

    const int N=21000;
    struct edge{int next,to,w;}a[N];
    int n,m,K[N],head[N],cnt,siz[N],rt,mx[N];
    int sum,ans[N],vis[N],P[N],cc;
    map<int,int> Map;
    void Getroot(int x,int fr)
    {
    	siz[x]=1;mx[x]=0;
    	for(int i=head[x];i;i=a[i].next)
    	{
    		int R=a[i].to;if(R==fr||vis[R]) continue;
    		Getroot(R,x);siz[x]+=siz[R];
    		mx[x]=max(mx[x],siz[R]);
    	}
    	mx[x]=max(mx[x],sum-siz[x]);
    	if(mx[x]<mx[rt]) rt=x;
    }
    void calcsum(int x,int fr)
    {
    	sum++;
    	for(int i=head[x];i;i=a[i].next)
    		if(a[i].to!=fr&&!vis[a[i].to]) calcsum(a[i].to,x);
    }
    void Getdis(int x,int d,int fr)
    {
    	P[++cc]=d;
    	for(int i=head[x];i;i=a[i].next)
    		if(!vis[a[i].to]&&a[i].to!=fr)
    			Getdis(a[i].to,d+a[i].w,x);
    }
    void solve(int x)
    {
    	rt=0;mx[0]=1e9;
    	sum=0;calcsum(x,0);
    	Getroot(x,0);
    	vis[x=rt]=1;Map.clear();
    	for(int i=head[x];i;i=a[i].next)
    	{
    		int R=a[i].to;if(vis[R]) continue;
    		cc=0;Getdis(R,a[i].w,x);
    		for(int j=1;j<=m;j++)
    			for(int w=1;w<=cc;w++)
    				if(Map.find(K[j]-P[w])!=Map.end()) ans[j]=1;
    		for(int w=1;w<=cc;w++) Map[P[w]]=1;
    	}
    	for(int j=1;j<=m;j++) if(Map.find(K[j])!=Map.end()) ans[j]=1;
    	for(int i=head[x];i;i=a[i].next)
    		if(!vis[a[i].to]) solve(a[i].to);
    }
    int main()
    {
    	cin>>n>>m;
    	for(int i=1;i<n;i++)
    	{
    		int x,y,w;scanf("%d%d%d",&x,&y,&w);
    		a[++cnt]=(edge){head[x],y,w};head[x]=cnt;
    		a[++cnt]=(edge){head[y],x,w};head[y]=cnt;
    	}
    	for(int i=1;i<=m;i++) cin>>K[i];
    	solve(1);
    	for(int i=1;i<=m;i++) ans[i]?puts("AYE"):puts("NAY");
    }
    
  • 相关阅读:
    随机二分图
    城市旅行
    JZPKIL
    线性基专题总结
    杜教筛专题总结
    [NOI2018]你的名字
    P1120 小木棍 [数据加强版]
    先序遍历
    P1736 创意吃鱼法
    P2258 子矩阵
  • 原文地址:https://www.cnblogs.com/xzyxzy/p/9903894.html
Copyright © 2011-2022 走看看