【题目链接】:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1008&cid=460
【解题思路】:
这题初看起来情况很复杂,动态性很高,但注意到一个很特殊的地方,就是他每次都是以MI这个为起点的,只要先从这个往下推,看得出的串有什么规律就行
了。这类题就是推导出数学式子,然后利用数学公式推导出条件。
1) 由MI 往下推可得MII,MIIII,MIIIIIIII……等后跟着2^n个I,而任意位置3个I可组成一个U,如果不消去UU,则以后每次翻倍后,把U换成I,则必然有2^n个I;
2) 而每次消去两个U,则以后再经翻倍,则少2^n - cur = 6*k,所以判断只要将M后的UI序列中U换成3个I,统计下I的个数,若能满足存在某个n 使2^n减去I的个数是6的倍数,
就可以判断其为满足条件的串(我刚开始鬼使神差的认为找到离I个数最近且比它大的2^n,潜意识不知道什么时候形成了这样的思维定势,结果wa了良久。这启示我在写代
码和尤其是检查代码时,不仅要看写的对不对,而且要对每一条语句问为什么是这样写,为什么不是那样写?这样会更好的避免思维定势)
3)设I的个数为x,U的个数为y,得出公式,2^n - (x+3y) = 6k ,则进一步化为2^n = x+3y+6k;我们来将右边构造出2^n,考察右边的式子:
首先(x+3y)%2==0 满足两边偶数的条件,另外3的倍数是得不到2^n的,所以(x+3y+6k)%3!=0;则 (x+3y)==6n1+2 或 (x+3y)==6n2+4 可以证明这两式都存在k可以满足、
2^n = x+3y+6k;简单点证明就是:
a.将上式同除以2,得到(x+3y)/2=3k+1 或(x+3y)/2=3k+2;令m=(x+3y)/2;(因为之前说过(x+3y)必须是偶数,所以m是整数)
得m=3k+1或m=3k+2;所以只需证明
所以限制条件即为(x+y)%2==0 && x%3 !=0 ;另外注意下边界情况。
PS:另外这题明显的按两种规则扩展每次I数量*2或减去6,明显的BFS扩展方式,由于数据不大,可一次BFS打表即可,判断时只需计算转化后I的总个数再看表中有无即可。
【借用同学的代码】:
#include<iostream>
#include<cstring>
using namespace std;
char str[1000010];
int main()
{
int n;
cin>>n;
while(n--)
{
cin>>str;
int l=strlen(str),xi=0,xu=0,k=0;
if(str[0]!='M')
{
cout<<"No"<<endl;
continue;
}
for(int i=1;i<l;i++)
{
if(str[i]=='I')
xi++;
else if(str[i]=='U')
xu++;
else
{
k=1;
break;
}
}
if(k==1 || ((xi+xu)%2==1 && !(xi==1 && xu==0)) || xi%3==0)
{
cout<<"No"<<endl;
continue;
}
else
{
cout<<"Yes"<<endl;
continue;
}
}
}