因为这题错失了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| eq |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} floor )。于是可以发现,在这种情况下,当(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} floor )也可以得到最优解。
剩下(x)为奇数,且(x equiv 10 (mod 11))的情况。一开始我的思路如下(可略过):
此时,若(x)的最后一位不为(9)或者(x)的倒数第二位为偶数,则可以令(a=lfloor frac{x}{2} floor, 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 (mod 11)),故只能让(|c|=|x|-2),令(a=lfloor frac{x}{2} floor, b=a+1)即可
最后只剩下(x)为奇数,(x equiv 10 (mod 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} floor, 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 (mod 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 %s %s ",t,t,t); return; } if(n==1){ printf("%d %d - ",(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 %s %s ",t,x,t,t); return; } if(s[n-1]!='9'){ Div(n,2); int m=strlen(t); printf("%s ",t); t[m-1]++; printf("%s ",t); t[m-1]='