http://codeforces.com/contest/625/problem/D
题意:
给你一个数字字符串s,长度1e6,算是一个大数吧,让你找到一个x,使得,x加上 逆转(x)=s
例如33,能找到 12,逆转(12)=21
12+21=33
输出的x不允许有前导零,例如输出 032是错的,只能输出32,如果输出320,她的逆转是023,他们的和是320+23
其实,X和逆转X就是一对回文串啦
一开始单纯地以为是回文串啦,其实还要考虑进位的问题
如 87+78 =165 ,7+8后会进位
总得来说,我们就是要把长度为n位的一个串拆成两个长度n位的回文串
我们要尽可能让 S 的第i位和第n-1-i位相同,只有这两位相同,才可能分解出两个一样的数字构成回文串
我们令l=头,r=尾
逐个比较, 如果 s[l]==s[r] ,则l,r向中间移动一格,
否则,我们看能否通过进位使得他们相等, 对于l,它左边是已经确定的,就别动了,只考虑l的往右退位,
同样,对于r,它的右边是确定的,只考虑r-1位的退位
也就是三种情况:
1、 左边l退位, if ( s[l] -1 == s[r] )
2、右边r-1退位,r增10 if (s[l] == s[r]+10 )
3、左右同时退位 if ( s[l]-1 == s[r] +10 )
如果满足哪种情况 则作相应操作,如果都不满足 则必然不可能构造出一个X
要注意的是 当r-l==1的时候, 情况1不可能成立,也就是不只左边往右退位,而右边不进位,情况3同理
判断完整个串后,还要考虑奇偶,如果是偶数长度必然没问题,如果是奇数长度,要看 最中间的那个位的数,是否为偶数,如果为奇数,则无法分解成2个回文串
至此,对于其余的位,只需要 靠左的第i位 构造为 (S【i】+1/)2,右边第n-1-i为s【i】/2
前导零的问题:
//我们求答案的过程0abc,cba0,这种情况是合法的,但是我们要输出cba0
//我们已经尽量让奇数的一边在前面了,如果得到的答案还有前导零,表明答案是,0ab0,的情况,这种情况两个数都有前导零,显然也不合法
------------以上的构造基于,长度为n位的一个串拆成两个长度n位的回文串
还有一种情况,长度为n位的穿 是由长度 n-1的回文串构造而成
如 78+87=165 ,这时我们要特判一下
即,先把 首位1,加到第二位,变成(16)(5)
然后再按照上面的方法再判断一次, 注意第一次的判断已经改变了子串的数字,需要copy一下备份
参考代码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const __int64 inf=2147483647;
const double pi=acos(-1.0);
double eps=0.000001;
__int64 min(__int64 a,__int64 b)
{return a<b?a:b;}
__int64 max(__int64 a,__int64 b)
{
return a<b?b:a;
}
int ans[200000+5];
char tm[200000+5];
int num[200000+5];
int check(int *t,int len)
{
int i;
for (i=0;i<=len/2;i++)
{
int l=i;
int r=len-1-i;
if (t[l]==t[r]) continue;
else
if (t[l]-1==t[r]&&r-l!=1) ///注意相邻的时候不能只左边往右退位,而右边不进位
{
t[l]--;
t[l+1]+=10;
}
else
if(t[l]-1==t[r]+10)
{
t[l]--;
t[l+1]+=10;
if (r-l==1)continue;
t[r]+=10;
t[r-1]--;
}
else if (t[l]==t[r]+10&& r-l!=1) //注意相邻
{
t[r]+=10;
t[r-1]--;
}
else
return 0;
}
if (len%2)
{
if ( t[len/2]%2 || t[len/2]>18 || t[len/2]<0 ) return 0;
ans[len/2]=t[len/2]/2;
}
for (i=0;i<len/2;i++)
{
if (t[i]>18 || t[i]<0 ) return 0;
ans[i]=(t[i]+1)/2;
ans[len-i-1]=t[i]/2;
}
return ans[0]>0 ;
//我们求答案的过程0abc,cba0,这种情况是合法的,但是我们要输出cba0
//我们已经尽量让奇数的一bian在前面了,如果得到的答案还有前导零,表明答案是,0ab0,的情况,这种情况两个数都有前导零,显然不合法
}
int main()
{
int i,j,k;
scanf("%s",tm+1);
int len=strlen(tm+1);
for (i=1;i<=len;i++)
num[i]=tm[i]-'0';
int flag=0;
if (check(num+1,len))
flag=1;
if (flag) //第一次判断成功,分解为2个长度len的回文串
{
for (i=0;i<len;i++)
printf("%d",ans[i]);
printf("\n");
}
else //第一次判断失败
{
for (i=1;i<=len;i++) //恢复原字符串
num[i]=tm[i]-'0';
if (num[1]==1) //首位是1,有可能是由长度len-1的两个回文串相加而成,特判
{
num[2]+=10;
if (check(num+2,len-1))
flag=1;
if (flag)
{
for (i=0;i<len-1;i++)
printf("%d",ans[i]);
printf("\n");
}
else
printf("0\n");
}
else
printf("0\n");
}
return 0;
}