zoukankan      html  css  js  c++  java
  • bzoj 4278 Tasowanie 后缀数组+贪心

    题目大意

    给定两个数字串A和B,通过将A和B进行二路归并得到一个新的数字串T,请找到字典序最小的T.(len leq 200000)

    题解

    我们从归并排序的角度去想,每次把两者之一较小的取出来
    遇到相等的元素的时候,排序时取那个都是一样的.
    但是在这道题中我们是求字典序最小,这就不一样了.
    我们不能任意取,这一步的决策其实是由后面的数字的大小决定的
    怎么说呢...
    方法蕴藏在这三张图里了,自己去悟吧。
    黑色箭头表示选取,黑色圆圈表示确定选取序列的理由.
    迭代第一次

    迭代第二次

    迭代第三次

    剩下的略...
    这个东西我们可以用后缀数组(lcp)来维护

    Code

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    inline void read(int &x){
    	x=0;char ch;bool flag = false;
    	while(ch=getchar(),ch<'!');if(ch=='-')ch=getchar(),flag = true;
    	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    const int maxn = 410010;
    int a[maxn],b[maxn],c[maxn];
    int wa[maxn],wb[maxn],ws[maxn];
    int sa[maxn],rank[maxn],height[maxn];
    inline bool cmp(int *r,int i,int j,int l){
    	return r[i] == r[j] && r[i+l] == r[j+l];
    }
    void da(int *r,int n,int m){
    	int i,j,p,*x = wa,*y = wb;
    	for(i=0;i<m;++i) ws[i] = 0;
    	for(i=0;i<n;++i) ws[x[i] = r[i]]++;
    	for(i=1;i<m;++i) ws[i] += ws[i-1];
    	for(i=n-1;i>=0;--i) sa[--ws[x[i]]] = i;
    	for(j=1,p=1;p<n;j<<=1,m=p){
    		for(p=0,i=n-j;i<n;++i) y[p++] = i;
    		for(i=0;i<n;++i) if(sa[i] >= j) y[p++] = sa[i] - j;
    		for(i=0;i<m;++i) ws[i] = 0;
    		for(i=0;i<n;++i) ws[x[y[i]]]++;
    		for(i=1;i<m;++i) ws[i] += ws[i-1];
    		for(i=n-1;i>=0;--i) sa[--ws[x[y[i]]]] = y[i];
    		for(swap(x,y),p=1,i=1,x[sa[0]] = 0;i<n;++i)
    		x[sa[i]] = cmp(y,sa[i-1],sa[i],j) ? p-1 : p++;
    	}
    }
    void get_h(int *r,int n){
    	int i,j,k=0;for(i=1;i<=n;++i) rank[sa[i]] = i;
    	for(i=0;i<n;height[rank[i++]] = k)
    	for(k ? --k : 0,j = sa[rank[i]-1];r[i+k] == r[j+k];++k);
    }
    int loger[maxn],minn[maxn][22];
    void pre(int *r,int n){
    	loger[1] = 0;
    	for(int i=2;i<=n;++i){
    		loger[i] = loger[i-1];
    		if( (1 << loger[i]+1 ) == i) ++loger[i];
    	}
    	for(int i=n;i>=1;--i){
    		minn[i][0] = r[i];
    		for(int j=1;(i + (1<<j) - 1) <= n;++j){
    			minn[i][j] = min(minn[i][j-1],minn[i+(1<<j-1)][j-1]);
    		}
    	}
    }
    int lcp(int s,int t){
    	s = rank[s];t = rank[t];
    	if(s+1 > t) swap(s,t);++s;
    	int k = loger[t-s+1];	
    	return min(minn[s][k],minn[t-(1<<k)+1][k]);
    }
    int n,m,len;
    inline bool order(int i,int j){
    	int k = lcp(i,j);
    	int x = (i+k) == n   ? 0x7f7f7f7f : c[i+k];
    	int y = (j+k) == len ? 0x7f7f7f7f : c[j+k];
    	return x <= y;
    }
    int q[maxn];
    int main(){
    	read(n);
    	for(int i=0;i<n;++i){
    		read(a[i]);
    		c[i] = a[i];
    	}c[n] = 1005;
    	read(m);
    	for(int i=0;i<m;++i){
    		read(b[i]);
    		c[n+i+1] = b[i];
    	}len = n+m+1;c[len] = 0;
    	da(c,len+1,1010);
    	get_h(c,len);
    	pre(height,len);
    	int i=0,j=0,k=0;
    	while(i < n && j < m){
    		int x = order(i,j+n+1);
    		if(x == 1) q[k++] = a[i++];
    		else q[k++] = b[j++];
    	}
    	while(i < n) q[k++] = a[i++];
    	while(j < m) q[k++] = b[j++];
    	for(i=0;i<k;++i) printf("%d ",q[i]);
    	getchar();getchar();
    	return 0;
    }
    
  • 相关阅读:
    MongoDB 释放磁盘空间 db.runCommand({repairDatabase: 1 })
    RK 调试笔记
    RK Android7.1 拨号
    RK Android7.1 移植gt9271 TP偏移
    RK Android7.1 定制化 itvbox 盒子Launcher
    RK Android7.1 双屏显示旋转方向
    RK Android7.1 设置 内存条作假
    RK Android7.1 设置 蓝牙 已断开连接
    RK Android7.1 进入Camera2 亮度会增加
    RK 3128 调触摸屏 TP GT9XX
  • 原文地址:https://www.cnblogs.com/Skyminer/p/6407439.html
Copyright © 2011-2022 走看看