• 【GDOI2017 day1】取石子游戏 线段树+区间合并


    题面

    如果给你一棵有根树,树根为 1,并且树的每个结点上有一个权值,现在我想知道每个点,除它所在子树以外的结点权值集合的 mex,怎么做呢?
    在这里,mex 是定义在集合上的函数,mex(S) 表示 S 这个集合中,最小的非负整数喔。
    对于 20% 的数据:N ≤ 500, T ≤ 20
    另外 50% 的数据:N ≤ 100000, T ≤ 5
    最后 30% 的数据:N ≤ 1000000, T ≤ 1

    100

    (O(nlogn))

    考虑把树上问题变为序列上的问题:
    每次询问相当于是抠掉dfs序列中的一段区间,然后询问前缀后缀;
    考虑给序列复制一遍,那么前缀和后缀拼起来就变成了一个区间。
    然后就变成了序列上的问题了。

    【清华集训2014】mex

    有一个长度为n的数组{a1,a2,...,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。
    

    我们构出权值主席树,每一位表示这一数字至此最后出现的位置。
    那么显然有种二分的做法。
    同时,离线可以把主席树变成线段树。

    Back to Problem

    然后这道题就等价于上述的那道题了。

    (O(n))

    枚举i从0到n,考虑那些树上结点的答案是当前枚举的i。
    我们给所有权值=i的结点做个LCA,这个LCA到根节点的路径上未赋值的答案都为i。
    然后利用并查集和tarjan求LCA,这是可以做到(O(n))(O(n*alpha))

    Code

    正确性未知,可能会被卡常的(O(nlogn))做法。

    #include<bits/stdc++.h>
    #define ll long long
    #define fo(i,x,y) for(int i=x;i<=y;i++)
    #define fd(i,x,y) for(int i=x;i>=y;i--)
    using namespace std;
    const int inf=0x7fffffff;
    const char* fin="2.in";
    const char* fout="2.out";
    const int maxn=1000007,maxm=maxn*2,maxt=maxn*4;
    int n,m,fi[maxn],la[maxm],ne[maxm],tot,num,a[maxn],w[maxm];
    int b[maxn][2],hd,tl,dfn[maxn],ffn[maxn],si[maxn];
    int c[maxt];
    struct Q{int l,r,id;}q[maxn];
    bool cmp(const Q &a,const Q &b){return a.r<b.r;}
    void modify(int l,int r,int t,int v,int vv){
    	int mid=(l+r)/2;
    	if (l==r){c[t]=vv;return;}
    	if (v<=mid) modify(l,mid,t*2,v,vv);
    	else modify(mid+1,r,t*2+1,v,vv);
    	c[t]=(c[t*2]>c[t*2+1]?c[t*2+1]:c[t*2]);
    }
    int query(int l,int r,int t,int v){
    	int mid=(l+r)/2;
    	if (l==r) return l;
    	if (c[t*2]<v) return query(l,mid,t*2,v);
    	else return query(mid+1,r,t*2+1,v);
    }
    void add_line(int a,int b){
    	tot++;
    	ne[tot]=fi[a];
    	la[tot]=b;
    	fi[a]=tot;
    }
    int read(){
    	int x=0;
    	char ch=getchar();
    	while (ch<'0' || ch>'9') ch=getchar();
    	while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    	return x;
    }
    void write(int x){
    	int w[20];
    	w[0]=0;
    	while (x) w[++w[0]]=x%10,x/=10;
    	fd(i,w[0],1) putchar(w[i]+'0');
    }
    void bfs(int v){
    	hd=tl=0;
    	b[++tl][0]=v;
    	b[tl][1]=0;
    	while (hd++<tl)
    		for(int k=fi[b[hd][0]];k;k=ne[k])
    			if (la[k]!=b[hd][1]) b[++tl][0]=la[k],b[tl][1]=b[hd][0];
    }
    void getdfn(){
    	bfs(1);
    	fd(i,tl,1){
    		int x=b[i][0],y=b[i][1];
    		si[x]=1;
    		for(int k=fi[x];k;k=ne[k]) if (la[k]!=y) si[x]+=si[la[k]];
    	}
    	fo(i,1,tl){
    		int x=b[i][0],y=b[i][1];
    		dfn[x]=ffn[x]=ffn[y]+1;
    		ffn[y]+=si[x];
    	}
    }
    void pre(){
    	scanf("%d%d",&n,&m);
    	tot=0;
    	memset(fi,0,sizeof fi);
    	memset(ffn,0,sizeof ffn);
    	fo(i,1,n) a[i]=read();
    	fo(i,1,m){
    		int j=read(),k=read();
    		add_line(j,k);
    		add_line(k,j);
    	}
    }
    int ans[maxn];
    void solve(){
    	fo(i,1,n) w[dfn[i]]=w[dfn[i]+n]=a[i]+1;
    	fo(i,1,4*n) c[i]=0;
    	fo(i,1,n) q[i].l=dfn[i]+si[i],q[i].r=dfn[i]+n-1,q[i].id=i;
    	sort(q+1,q+n+1,cmp);
    	int j=1;
    	fo(i,1,n){
    		while (j<=q[i].r) modify(1,maxn,1,w[j],j),j++;
    		ans[q[i].id]=query(1,maxn,1,q[i].l)-1;
    	}
    	fo(i,1,n)write(ans[i]),putchar(' ');
    	printf("
    ");
    }
    int main(){
    	freopen(fin,"r",stdin);
    	freopen(fout,"w",stdout);
    	int t=read();
    	while (t--){
    		pre();
    		getdfn();
    		solve();
    	}
    	return 0;
    }
    
  • 相关阅读:
    进程同步和死锁;事务、悲观锁、乐观锁、表锁、行锁
    快速排序
    oracle索引分类
    数据结构
    MySQL视图
    sql优化的方法
    MySQL索引
    转:关于ROWNUM的使用
    转载:mybatis踩坑之——foreach循环嵌套if判断
    Spring 注意事项
  • 原文地址:https://www.cnblogs.com/hiweibolu/p/6810315.html
Copyright © 2020-2023  润新知