2016.10.25 解题报告
Part.1 珠(beads)
水题一道,有坑。(因为变量没赋初值直接爆零了。。。TvT。。。)
解题思路
1. 读入用string(等于没说),C党注意string相关函数的用法;
2. 把所给序列复制一遍,这样就能枚举环上的每个断点了;
3. 从左往右搜。设一个bool变量beg,有2打头为true,否则为false;一个计数变量cnt,记录当前合法的连续3的长度;ans记录最长的合法连续3的长度(这俩变量都不计2)
① s[i]==’2’&&beg==false: beg=true,跳过;
② s[i]!=’2’&&beg==false: 跳过;
③ beg==true
s[i]==’2’: 比较cnt和ans,更新ans;beg=true;cnt清零;跳过;
s[i]==’3’: cnt++;跳过;
其他:比较cnt和ans,更新ans;beg=false;cnt清零。
4. 从右往左搜,同上;
5. 注意:① 序列长度为1时,如果是2,输出2,否则非法(输出TvT)
② 序列中有单独的2时要特判。
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
int main()
{
string s;
cin>>s;
bool beg=0;
int cnt=0,ans=0;
if (s.length()==1&&s[0]=='2') {cout<<2; return 0;}
if (s.length()==1&&s[0]!='2') {cout<<"TvT"; return 0;}
s+=s;
int k=s.length();
int i=0;
bool ff=0;
while (i<k)
{
if (beg==0&&s[i]=='2') { beg=1; ff=1; i++; continue; }
if (beg==0&&s[i]!='2') { i++; continue; }
if (beg==1)
{
if (s[i]!='3'&&s[i]!='2')
{
if (cnt>ans) ans=cnt;
beg=0; i++; cnt=0;
continue;
}
if (s[i]=='2')
{
if (cnt>ans) ans=cnt;
cnt=0; beg=1; i++;
continue;
}
if (s[i]=='3') { cnt++; i++; }
}
}
i=k-1; beg=0; cnt=0;
while (i>=0)
{
if (beg==0&&s[i]=='2') { beg=1; i--; continue; }
if (beg==0&&s[i]!='2') { i--; continue; }
if (beg==1)
{
if (s[i]!='3'&&s[i]!='2')
{
if (cnt>ans) ans=cnt;
beg=0; i--; cnt=0;
continue;
}
if (s[i]=='2')
{
if (cnt>ans) ans=cnt;
cnt=0; beg=1; i--;
continue;
}
if (s[i]=='3') {cnt++; i--;}
}
}
if (!ans&&!ff) {cout<<"TvT"; return 0;}
else {cout<<2; for (int i=1;i<=ans;i++) cout<<3;}
return 0;
}
Part.2 免农(radit)
改编(感觉有点恶搞的意思)自NOI2011第一试“兔农”一题,但是要比原题简单很多。。。原题需要矩乘求斐波那契数列类似物,但这道题只需要快速幂就能解决了。
解题思路
参考官方题解,就不再重复了
while (m<=n) //一定要加等号
{
if (tmp==1) //说明有一只免子落单了
{
ans=((power(2,m+1)+p-1)%p*power(2,n-m)%p)%p;
//死掉一只免子,从这时往后就倍增,快速幂跑就行了
cout<<ans;
return 0;
}
tmp=tmp*2%k; //找什么时候出现落单的免子
m++;
}
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
long long n,k,p,ans=0;
long long power(long long x,long long y)
{
long long re=1,i=y;
for (;i;i=i>>1,x=x*x%p)
if (i&1)
re=re*x%p;
return re%p;
}
int main()
{
cin>>n>>k>>p;
if (k%2==0)
{
ans=power(2,n+1)%p;
cout<<ans;
return 0;
}
long long m=0,tmp=2;
while (m<=n)
{
if (tmp==1)
{
ans=((power(2,m+1)+p-1)%p*power(2,n-m)%p)%p;
cout<<ans;
return 0;
}
tmp=tmp*2%k;
m++;
}
cout<<power(2,n+1);
return 0;
}
Part.3高维网络(cube)(没AC,想看满分解法的童鞋请直接跳到官方题解,或直接跳到本文最后)
分情况讨论,很多很多情况。。。按照给出的测试点情报,理论上是可以拿到相当可观的分数的。本蒟蒻撑死跑到75分(虽然官方说这样搞应该可以搞到80分来着。。。),100分解法参考官方题解。。。
Point.1 d==1 2点可过
D==1的时候就是一条线啊!直接判断目标点到(0,0)之间有没有断开就行啦。。。
这种情况下会有神奇的坑,如果算法不合适的话p==0可能会出错,最好特判一下。
Point.2 d==2 p==0 4点可过
(之所以不把p==0都列成一类主要是因为d==1太简单。。。)
根据题意,相当于在图中绿色矩形框内由左下角走到右上角,本来是个典型的棋盘DP,然而既然没有不能走的点,根据数学原理,结果等于C(x+y,x)(或者C(x+y,y)二者相等):一共走x+y步,其中有x步向右走。
Point.3 d==2 p>0 4点可过
p>0的时候,显然不能直接用组合数,那就老老实实跑DP吧。。。节省内存可以只开一个二维数组,如果不能走就设成-1,转移时跳过就好。
(惭愧的是,第15个点并不能用这种解法跑出来,不想爆空间直接MLE就别作大死开100,000*100,000的数组。。。所以正解还是参考官方题解吧TAT。。。)
Point.4 d==3 p==0 2点可过
好吧分这么多情况实在太作死了。。。但为了多拿几分再麻烦也要无怨无悔才行。。。比如第13、14、16个点拿DP是没法跑的(炸内存),但拿组合数就可以过。三维的组合数算法一样,只不过每次有三个方向可以走,答案很显然。
long longp1=C(n,sx)%mod;
long long p2=C(n-sx,sy)%mod;
ans=p1*p2%mod;
Point.4 d==3 p>0 3点可过
还是棋盘型DP,第17个点同样搞不掉。。。三维数组开到100就快撑着了,100,000就不要想了。。。
综上,靠着分类处理(骗分大法好!)可以搞掉15个点,剩下那5个。。。本蒟蒻实在啃不动,求大神指教。。。
75分代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
int d,p;
const long long mod=1000000007;
long long jc[100005];
long long f[1001][1001],f3[101][101][101];
void jc_(int n)
{
jc[1]=1;
for (int i=2;i<=n;i++)
jc[i]=jc[i-1]*i%mod;
}
void plan_1()
{
int sx=0,x=0,mi;
cin>>sx;
for (int i=1;i<=p;i++)
{
cin>>x;
if (x<mi) mi=x;
}
if (sx<x||p==0) cout<<1;
else cout<<0;
return;
}
long long power(long long x)
{
long long re=1,i=mod-2;
for (;i;i=i>>1,x=x*x%mod)
if (i&1) re=re*x%mod;
return re;
}
long long C(int n,int m)
{
if (m==0) return 1;
long long k=((jc[n]*power(jc[m]))%mod*power(jc[n-m]))%mod;
return k;
}
void plan_2()
{
long long ans;
int sx,sy;
cin>>sx>>sy;
int n=sx+sy;
jc_(n);
ans=C(n,min(sx,sy));
if (p==0)
{
cout<<ans;
return;
}
if (p==1)
{
int x,y;
for (int i=1;i<=p;i++)
{
cin>>x>>y;
int m=x+y;
long long p1=C(m,min(x,y)),p2=C(n-m,min(sx-x,sy-y));
if(ans-p1*p2>=0) ans=ans-p1*p2;
else ans=(ans-p1*p2+mod)%mod;
}
cout<<ans%mod;
return;
}else
{
int x,y;
memset(f,0,sizeof(f));
for (int i=1;i<=p;i++)
{
cin>>x>>y;
f[x][y]=-1;
}
f[0][0]=1;
for (int i=0;i<=sx;i++)
for (int j=0;j<=sy;j++)
{
if (f[i][j]==-1) continue;
if (f[i-1][j]!=-1&&i-1>=0) f[i][j]=(f[i][j]+f[i-1][j])%mod;
if (f[i][j-1]!=-1&&j-1>=0) f[i][j]=(f[i][j]+f[i][j-1])%mod;
}
cout<<f[sx][sy]%mod;
return;
}
}
void plan_3()
{
long long ans;
int sx,sy,sz;
<span style="white-space:pre"> </span>cin>>sx>>sy>>sz;
int n=sx+sy+sz;
jc_(n);
if (p==0)
{
long long p1=C(n,sx)%mod;
long long p2=C(n-sx,sy)%mod;
ans=p1*p2%mod;
cout<<ans;
return;
}else
{
int x,y,z;
memset(f3,0,sizeof(f3));
for (int i=1;i<=p;i++)
{
cin>>x>>y>>z;
f3[x][y][z]=-1;
}
f3[0][0][0]=1;
for (int i=0;i<=sx;i++)
for (int j=0;j<=sy;j++)
for (int k=0;k<=sz;k++)
{
if (f3[i][j][k]==-1) continue;
if (f3[i-1][j][k]!=-1&&i-1>=0) f3[i][j][k]=(f3[i][j][k]+f3[i-1][j][k])%mod;
if (f3[i][j-1][k]!=-1&&j-1>=0) f3[i][j][k]=(f3[i][j][k]+f3[i][j-1][k])%mod;
if (f3[i][j][k-1]!=-1&&k-1>=0) f3[i][j][k]=(f3[i][j][k]+f3[i][j][k-1])%mod;
}
cout<<f3[sx][sy][sz]%mod;
return;
}
}
int main()
{
cin>>d>>p;
if (d==1) plan_1();
if (d==2) plan_2();
if (d==3) plan_3();
if (d>3) cout<<0;
return 0;
}
官方满分解法如下(似乎是离散化后用一维数组跑,挺玄妙的。。。)