zoukankan      html  css  js  c++  java
  • 题解「CF1329D Dreamoon Likes Strings」

    这题有一个显而易见的结论:将一个串划分成若干个美丽的子串,用最少的次数把这些串取完则是最优方案。可以证明不会有比这种取法更优的方案。

    更形式地描述,将一个串划分成若干个子串的过程,就是找到所有 (a_i=a_{i+1})(i)。对于第 (x) 个这样的 (i),将其记为 (p_x)。那么从左向右数的第 (x) 个美丽的子串就为 (a[p_{x-1}+1..p_x])

    经过手玩之后我们可以发现,如果把每个美丽的子串用它的最后一个字母代表,构成一个新串 (b),即(b_i=a_{p_i})那么在原串 (a) 上 取一个美丽的子串的操作就变为下述两种操作:

    • 在新串 (b) 上取一个字符。
    • 在新串 (b) 上取两个 相邻但不相同 的字符。

    怎么理解这样的两个操作呢?

    对于第一种操作,分两种情况讨论:

    • 在原串上取了头尾的子串,显然在 (b) 上的影响为被取走一个字符。
    • 在原串上取了中间的子串,但是对于被取走的子串 (a[p_{i-1}+1,p_i]),有 (a_{p_{i-1}}=a_{p_i+1})。经过分析很容易得到,这就是第二个操作去掉 不相同约束 的情况。

    经过上述讨论自然可以得出, (b) 上的一次上述操作对应原串 (a) 上的一次操作。

    那么问题就转化为:在一个字符串上,每次可以取走一个字符,或者 取走两个相邻但不相同 的字符,每次进行一次操作后,将剩余的字符按原来顺序连接起来,求最少多少次能把这个字符串取空。

    依然是分类讨论,记 (b) 中最多出现的字符次数为 (mx)(b) 的长度为 (sum),则:

    • (frac{mx}{2}>sum),由于第二个操作更优,尽可能使用第二个操作令其他字符与最多出现的字符配对,如果不再有字符能与最多出现的字符配对,则将剩下的字符全部使用第一个操作取完。

    • (frac{mx}{2}leq sum),由于第二个操作更优,我们会尽可能使用第二个操作,令不相同的两个字符配对。直到当前的 (frac{mx}{2}>sum),转化为第一种情况。

    经过手玩可以发现上述过程与括号匹配很像,可以使用栈实现上述过程。

    (b) 串中默认忽略了最后一个子串,所以最后 (ans=max(lceilfrac{sum}{2} ceil,mx)+1),还需要最后一次删除将最后那个串删掉。

    由于最后输出方案时需要输出每次删除后的位置而不是初始位置,可以使用线段树维护,区间赋值+区间查询+单点查询即可。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define mx tot[res]
    #define max(a,b) ((a)>(b)? (a):(b))
    #define min(a,b) ((a)<(b)? (a):(b))
    struct node {int l,r;}tmp[200005];
    char a[200005],b[200005];
    int tag[800005],sum[800005];
    int tot[355],resL[200005],resR[200005],st[200005],p[200005],tmpL[200005],tmpR[200005];
    inline bool cmp1(node x,node y) {return x.l<y.l;}
    inline bool cmp2(node x,node y) {return x.r>y.r;}
    inline void build(int p,int l,int r) {
    	tag[p]=-1;sum[p]=0;
    	if(l==r) {sum[p]=1;return;}
    	int mid=l+r>>1;
    	build(p<<1,l,mid);
    	build(p<<1|1,mid+1,r);
    	sum[p]=sum[p<<1]+sum[p<<1|1];
    }
    inline void spread(int p,int l,int r) {
    	int mid=l+r>>1;
    	if(tag[p]!=-1) {
    		tag[p<<1]=tag[p<<1|1]=tag[p];
    		sum[p<<1]=(mid-l+1)*tag[p];
    		sum[p<<1|1]=(r-mid)*tag[p];
    		tag[p]=-1;
    	}
    }
    inline void change(int p,int l,int r,int L,int R,int val) {
    	if(L<=l&&r<=R) {sum[p]=(r-l+1)*val;tag[p]=val;return;}
    	spread(p,l,r);
    	int mid=l+r>>1;
    	if(L<=mid) change(p<<1,l,mid,L,R,val);
    	if(R>mid) change(p<<1|1,mid+1,r,L,R,val);
    	sum[p]=sum[p<<1]+sum[p<<1|1];
    }
    inline int ask(int p,int l,int r,int L,int R) {
    	if(L<=l&&r<=R) return sum[p];
    	spread(p,l,r);
    	int mid=l+r>>1,res=0;
    	if(L<=mid) res+=ask(p<<1,l,mid,L,R);
    	if(R>mid) res+=ask(p<<1|1,mid+1,r,L,R);
    	return res;
    }
    inline int askPos(int p,int l,int r) {
    	if(l==r) return l;
    	int mid=l+r>>1;
    	if(sum[p<<1]) return askPos(p<<1,l,mid);
    	else return askPos(p<<1|1,mid+1,r);
    }
    int main() {
    	int T;
    	scanf("%d",&T);
    	while(T--) {
    		int top=0,m=0,num=0,len=0,ans=0,res=0,now,pos1,pos2;
    		scanf("%s",a+1);
    		len=strlen(a+1);
    		for(register char ch='a';ch<='z';++ch) tot[ch]=0;
    		for(register int i=1;i<=len;++i) if(a[i]==a[i+1]) {++tot[a[i]];p[++num]=i;b[num]=a[i];}
    		m=num;pos1=1;pos2=len;
    		//for(register int i=1;i<=num;++i) printf("%d
    ",p[i]);
    		build(1,1,len);
    		//std::swap(b[num],b[num-1]);std::swap(p[num],p[num-1]);
    		//if(num&1) {resL[++ans]=1;resR[ans]=p[1];tmpL[ans]=1;tmpR[ans]=p[1];change(1,1,len,1,p[1],0);--tot[b[1]];--m;}
    		//if(num&1) {resL[++ans]=p[num]+1;resR[ans]=len;tmpL[ans]=p[num]+1;tmpR[ans]=len;change(1,1,len,p[num]+1,len,0);--tot[b[num]];--num;--m;}
    		//if(num&1) {resL[++ans]=p[num-1]+1;resR[ans]=p[num];tmpL[ans]=resL[ans];tmpR[ans]=resR[ans];change(1,1,len,resL[ans],resR[ans],0);--tot[b[num-1]];--m;}
    		for(now=1;now<=num;++now) {
    			//if(now==num-1) continue;
    			for(register char ch='a';ch<='z';++ch) res=tot[res]>tot[ch]? res:ch;
    			if(2*mx>=m) break;
    			if(!top) {st[++top]=now;}
    			else {
    				if(b[st[top]]==b[now]) {st[++top]=now;}
    				else {
    					--tot[b[st[top]]];--tot[b[now]];
    					resL[++ans]=ask(1,1,len,1,p[st[top]]+1);resR[ans]=ask(1,1,len,1,p[now]);
    					tmpL[ans]=p[st[top]]+1;tmpR[ans]=p[now];
    					change(1,1,len,p[st[top]]+1,p[now],0);--top;m-=2;
    				}
    			}
    		}
    		if(m) {
    			for(;now<=num;++now) {
    				if(!top||b[st[top]]==b[now]||(b[st[top]]!=res&&b[now]!=res)) {st[++top]=now;}
    				else {
    					--tot[b[st[top]]];--tot[b[now]];
    					resL[++ans]=ask(1,1,len,1,p[st[top]]+1);resR[ans]=ask(1,1,len,1,p[now]);
    					tmpL[ans]=p[st[top]]+1;tmpR[ans]=p[now];
    					change(1,1,len,p[st[top]]+1,p[now],0);--top;m-=2;
    				}
    			}
    		}
    		for(register int t=1;t<=top;++t) {
    			int pos=askPos(1,1,len);
    			--tot[b[st[t]]];
    			resL[++ans]=ask(1,1,len,1,pos);
    			resR[ans]=ask(1,1,len,1,p[st[t]]);
    			tmpL[ans]=pos;tmpR[ans]=p[st[t]];
    			change(1,1,len,pos,p[st[t]],0);
    		}
    		printf("%d
    ",ans+1);
    		for(register int i=1;i<=ans;++i) {printf("%d %d
    ",resL[i],resR[i]);tmp[i].l=tmpL[i];tmp[i].r=tmpR[i];}
    		//for(register int i=1;i<=ans;++i)  {printf("%d %d
    ",tmpL[i],tmpR[i]);tmp[i].l=tmpL[i];tmp[i].r=tmpR[i];}
    		//pos1=(resL[i]<=pos1? max(pos1,resR[i]+1):pos1);(pos2=resR[i]>=pos2? min(pos2,resL[i]-1):pos2);
    		std::sort(tmp+1,tmp+1+ans,cmp1);
    		for(register int i=1;i<=ans;++i) pos1=(tmp[i].l<=pos1? max(pos1,tmp[i].r+1):pos1);
    		std::sort(tmp+1,tmp+1+ans,cmp2);
    		for(register int i=1;i<=ans;++i) pos2=(tmp[i].r>=pos2? min(pos2,tmp[i].l-1):pos2);
    		pos1=max(pos1,1);pos2=min(pos2,len);
    		//printf("%d %d
    ",pos1,pos2);
    		//for(register int i=1;i<=ans;++i) printf("%d %d
    ",resL[i],resR[i]);
    		printf("%d %d
    ",ask(1,1,len,1,pos1),ask(1,1,len,1,pos2));
    	}
    	return 0;
    }
    
  • 相关阅读:
    KDJ回测
    利用网易获取所有股票数据
    利用东方财富网获取股票代码
    python发邮件
    用指针向数组插入元素
    冒泡排序
    Hadoop的安装与配置
    关于执行memcached报错问题
    tomcat Linux安装
    网易CentOS yum源
  • 原文地址:https://www.cnblogs.com/tommy0103/p/12992021.html
Copyright © 2011-2022 走看看