CH Round #54 - Streaming #5 (NOIP模拟赛Day1) 解题报告

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;
}

官方满分解法如下似乎是离散化后用一维数组跑,挺玄妙的。。。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值