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;
    }
    
  • 相关阅读:
    ExtJS小技巧
    Oracle 表的行数、表占用空间大小,列的非空行数、列占用空间大小 查询
    NPM 私服
    IDEA 不编译java以外的文件
    SQL 引号中的问号在PrepareStatement 中不被看作是占位符
    Chrome 浏览器自动填表呈现淡黄色解决
    批量删除Maven 仓库未下载成功.lastupdate 的文件
    Oracle 11g 监听很慢,由于监听日志文件太大引起的问题(Windows 下)
    Hibernate 自动更新表出错 建表或添加列,提示标识符无效
    Hibernate 自动更新表出错 More than one table found in namespace
  • 原文地址:https://www.cnblogs.com/tommy0103/p/12992021.html
Copyright © 2011-2022 走看看