[2020 小米邀请赛][E. Rikka with Subsequence]
因为这题错失了1k RMB+小米手表,心塞塞...
题目链接:https://ac.nowcoder.com/acm/contest/9328/E
题目大意:给定一个数字\(x\),\(x\)的长度不超过\(5000\)位,设有两个数\(a,b\)满足\(a+b=x\),\(a\)与\(b\)的字符串形式的最长公共子序列为\(c\),要求输出一个使\(c\)的长度尽可能长的方案
题解:记\(|x|\)表示数字\(x\)的长度,考虑\(|c|\)的理论最大值
显然,当且仅当\(x[0]>1\)且\(x\)为偶数时,可以取到\(|c|=|x|\)
若\(x\)为偶数,且\(|c| \neq |x|\),那么此时一定有\(x[0]=1\),可以得出最优解是\(a=b=\frac{x}{2}\),此时\(|c|=|x|-1\)
若\(x\)为奇数,那么一定有\(|c|\le |x|-1\),假设有\(b=10a+k(k<10)\)使得\(a+b=x\),那么可以发现当\(x\)对\(11\)取模的值不为\(10\)时,有\(|c|=|a|\)的解,其中,\(a=\lfloor \frac{x}{11} \rfloor \)。于是可以发现,在这种情况下,当\(x\)的前两位的值大于等于\(11\)时,可以取到\(|c|=|x|-1\),否则会有以下两种情况:
\(x\)为个位数,此时可以特判
\(x\)是\(10\)开头的,可以发现,这个时候\(|a|\)与\(|b|\)的取值只可能是:\(|a|=|x|, |b|\le |x|-2\)或\(|a|=|x|-1, |b|\le |x|-1\)(这里不妨设\(|a|\ge|b|\))。在第一种情况下,有\(|c|\le |x|-2\),在第二种情况下,由于\(x\)为奇数,如果\(|c|=|x|-1=|a|\)则会推出\(a=b\),从而导致矛盾。因此,此时的最优解只可能是\(|c|=|x|-2\),同样取\(a=\lfloor \frac{x}{11} \rfloor \)也可以得到最优解。
剩下\(x\)为奇数,且\(x \equiv 10 (\bmod 11)\)的情况。一开始我的思路如下(可略过):
此时,若\(x\)的最后一位不为\(9\)或者\(x\)的倒数第二位为偶数,则可以令\(a=\lfloor \frac{x}{2}\rfloor, b=a+1\),此时有\(|c|=|a|-1\)。当\(x[0]>1\)时,\(|c|=|x|-1\),否则当\(x[0]=1\)时,考虑是否还有让\(|c|=|x|-1\)的可能
在这种情况下,考虑\(|a|\)与\(|b|\)的取值,可以发现,有\(|a|=|x|, |b|\le |x|-1\)或\(|a|=|x|-1, |b|\le |x|-1\)。显然由于\(x\)为奇数,第二种情况下不能取到最优解。而在第一种情况下,由于要取到\(|c|=|x|-1\),因此\(a\)只可能是\(b\)中间再塞进去一位。而又因为\(x\)是奇数,所以这一位只可能是最后一位(否则两个数个位相同相加为偶数),故只可能是\(b=10a+k(k<10)\)的形式。但此时已有结论\(x \equiv 10 (\bmod 11)\),故只能让\(|c|=|x|-2\),令\(a=\lfloor \frac{x}{2}\rfloor, b=a+1\)即可
最后只剩下\(x\)为奇数,\(x \equiv 10 (\bmod 11)\),且\(x\)的末两位为\(y9\)(\(y\)为奇数)的情况。如果\(x\)的倒数第三位为偶数,则可以将前面的部分和后面这两位分开,其中前面的部分除以二,后面的部分按照模\(11\)小于\(10\)来处理,这样可以证明是最优解(证明方法同上,此时\(|c|=|x|-1\)或\(|x|-2\))
若\(x\)的倒数第三位为奇数,则可以让前面的部分除以二向下取整,末尾分别拼上二位数\(p,q\)使得\(p+q=1y9\),且\(p\)与\(q\)有一位相同。这时会发现除了\(y=9\)其余情况均能处理,于是考虑换一种思路
如果\(x\)的最后一位不为\(9\),同样令\(a=\lfloor \frac{x}{2}\rfloor, b=a+1\)。否则,则有\(str(x)=str(y)+str(c)+str(99...9)\),若\(y\)为偶数,则令\(str(a)=str(\frac{y}{2})+str(c)+str(0909..09),str(b)=str(\frac{y}{2})+str(0)+str(9090..90)\)可得最优解(对于\(x[0]=1\)的情况讨论不再赘述,其实可以发现若\(x[0]=1\),\(x\)为奇数,且\(x \equiv 10 (\bmod 11)\),\(|c|\le |x|-2\)一定成立)。若\(y\)为奇数,则令\(str(a)=str(\frac{y-1}{2})+str(c+1)+str(9090..90),str(b)=str(\frac{y-1}{2})+str(9)+str(0909..09)\)即可,需要注意的是如果\(y=1\)则不能将前导零输出
#include<bits/stdc++.h> using namespace std; #define N 5050 int T,n; char s[N],t[N]; void Div(int n,int x) { int m=strlen(t); memset(t,0,sizeof(char)*(m+5)); int r=0,j=0; for(int i=0;i<n;i++){ r=(r*10+s[i]-'0'); if(r>=x || j>0){ t[j++]=r/x+'0'; r%=x; } } if(j==0)t[j++]='0'; } void init() { scanf("%s",s); n=strlen(s); if(s[n-1]%2==0){ Div(n,2); printf("%s\n%s\n%s\n",t,t,t); return; } if(n==1){ printf("%d\n%d\n-\n",(s[0]-'0')/2,(s[0]-'0')/2+1); return; } int x=0; for(int i=0;i<n;i++)x=(x*10+s[i]-'0')%11; if(x<10){ Div(n,11); printf("%s%d\n%s\n%s\n",t,x,t,t); return; } if(s[n-1]!='9'){ Div(n,2); int m=strlen(t); printf("%s\n",t); t[m-1]++; printf("%s\n",t); t[m-1]='\0'; printf("%s\n",t); return; } int m=0; while(s[n-1]=='9')n--,m++; int k=s[n-1]-'0'; n--; if(s[n-1]%2==0){ Div(n,2); printf("%s%d",t,k); for(int i=0;i<m;i++)printf("%d",i&1?9:0); printf("\n%s",t); for(int i=0;i<=m;i++)printf("%d",i&1?9:0); printf("\n%s",t); for(int i=0;i<m;i++)printf("%d",i&1?9:0); printf("\n"); return; } Div(n,2); if(t[0]!='0')printf("%s",t); printf("%d",k+1); for(int i=0;i<m;i++)printf("%d",i&1?0:9); printf("\n"); if(t[0]!='0')printf("%s",t); for(int i=0;i<=m;i++)printf("%d",i&1?0:9); printf("\n"); if(t[0]!='0')printf("%s",t); for(int i=0;i<m;i++)printf("%d",i&1?0:9); printf("\n"); } int main() { scanf("%d",&T); while(T--)init(); }