NOIP2016--day2 总结&&题解

T1–组合数问题

在这里插入图片描述
.
.
.
一开始看到这题目,哎呀,懵逼数学题。然后…,不管了打个表找个规律先。
然后发现用2000*2000的数组存下所有组合数不是不行,然后突然又想到,这么大的数字,long long 也会爆啊,然后,猛然发现可以对每一个组合数进行取模。
所以我们需要的公式是 a c [ i ] [ j ] = ( c [ i − 1 ] [ j − 1 ] + c [ i − 1 ] [ j ] ) % k ac[i][j]=( c[i-1][j-1] +c[i-1][j] )\%k ac[i][j]=(c[i1][j1]+c[i1][j])%k
这不就是杨辉三角的公式嘛!!!!

然后…,开开心心一发暴力求每一组询问
好了 90分
emmmmmmmmmm
.
.
.
.
好了说正解,用二维前缀和存下对于每一个n,m对应的答案,然后 O ( n ) O(n) O(n)查询,好了,AC。
上代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#define LL long long
using namespace std;
int c[2010][2010],s[2010][2010],n,m,k,t;
void first()
{
	c[0][0]=1;
	c[1][1]=1;
	for(int i=1;i<=2000;i++)
	{
		c[i][0]=1;
	}
	for(int i=2;i<=2000;i++)
	{
		for(int j=1;j<=i;j++)
		{
			c[i][j]=( c[i-1][j-1] +c[i-1][j] )%k;
		}
	}
	
	for(int i=2;i<=2000;i++)
	{
		for(int j=1;j<=i;j++)
		{
			s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
			if(c[i][j]==0) s[i][j]++;
		}
		s[i][i+1]=s[i][i];
	}
}
int main()
{
	//freopen("problem.in","r",stdin);
	//freopen("problem.out","w",stdout);
	memset(s,0,sizeof(s));
	memset(c,0,sizeof(c));
	scanf("%d %d",&t,&k);
	first();
	while(t)
	{
		t--;
		scanf("%d %d",&n,&m);
		if(n<m) m=n;//很重要 
		printf("%d\n",s[n][m]);
	}
	return 0;
}

.
.
.
.
.

T2–蚯蚓

在这里插入图片描述
在这里插入图片描述
输入输出样例
输入样例#1: 复制
3 7 1 1 3 1
3 3 2
输出样例#1: 复制
3 4 4 4 5 5 6
6 6 6 5 5 4 4 3 2 2
输入样例#2:
3 7 1 1 3 2
3 3 2
输出样例#2:
4 4 5
6 5 4 3 2
输入样例#3:
3 7 1 1 3 9
3 3 2
输出样例#3:
//空行
2
说明
【样例解释1】

在神刀手到来前:33只蚯蚓的长度为3,3,23,3,2。

11秒后:一只长度为33的蚯蚓被切成了两只长度分别为11和22的蚯蚓,其余蚯蚓的长度增加了11。最终44只蚯蚓的长度分别为(1,2),4,3(1,2),4,3。括号表示这个位置刚刚有一只蚯蚓被切断

22秒后:一只长度为44的蚯蚓被切成了11和33。55只蚯蚓的长度分别为:2,3,(1,3),42,3,(1,3),4。

3秒后:一只长度为44的蚯蚓被切断。66只蚯蚓的长度分别为:3,4,2,4,(1,3)3,4,2,4,(1,3)。

44秒后:一只长度为44的蚯蚓被切断。77只蚯蚓的长度分别为:4,(1,3),3,5,2,44,(1,3),3,5,2,4。

55秒后:一只长度为55的蚯蚓被切断。88只蚯蚓的长度分别为:5,2,4,4,(1,4),3,55,2,4,4,(1,4),3,5。

66秒后:一只长度为55的蚯蚓被切断。99只蚯蚓的长度分别为:(1,4),3,5,5,2,5,4,6(1,4),3,5,5,2,5,4,6。

77秒后:一只长度为66的蚯蚓被切断。1010只蚯蚓的长度分别为:2,5,4,6,6,3,6,5,(2,4)2,5,4,6,6,3,6,5,(2,4)。所以,77秒内被切断的蚯蚓的长度依次为3,4,4,4,5,5,63,4,4,4,5,5,6。77秒后,所有蚯蚓长度从大到小排序为6,6,6,5,5,4,4,3,2,26,6,6,5,5,4,4,3,2,2
【样例解释2】

这个数据中只有t=2t=2与上个数据不同。只需在每行都改为每两个数输出一个数即可。

虽然第一行最后有一个66没有被输出,但是第二行仍然要重新从第二个数再开始输出。

【样例解释3】

这个数据中只有t=9t=9与上个数据不同。

注意第一行没有数要输出,但也要输出一个空行。

【数据范围】
在这里插入图片描述

好了,当时一看到这题目直接懵逼,然后手动模拟了一下。果断用优先队列暴力了一发。
嗯,35分炸掉了。炸掉的主要点还是没有想清楚如何把每一次加上q这一步给优化一下,导致了我的暴力代码中每一次处理一只蚯蚓都要把队列中的所有数字都拿出加一下,再放回去。
.
.
.
.
那重点来了,如何处理每次的将每只蚯蚓加一定长度呢?或许可以转变思路, 每次只有两只蚯蚓没被加其他的全部被加了, 根据运动是相互的, 除了那被切成的两只蚯蚓其他的都往正方向移动了一些, 等价于那两只往负方向移动了一些. 所以可以记录累计加的长度, 有几只没被加的就减去就好了…这样处理加上优先队列是可以拿到85分的,还不错.IPG
先上个85分的代码吧

#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
int n,m,q,t;
int u,v,add=0,x,y,z;
double p;
priority_queue<int>s;
int a[10100000],ans1[10000010];
int ans2[10000001];
void work()
{
	for(int l=1;l<=m;l++)
	{
		int x=s.top()+add;
		s.pop();
		add+=q;
		ans1[l]=x;
		int a1=floor(p*(double)x),a2=x-a1;
		a1-=add;
		a2-=add;
		s.push(a1);
		s.push(a2);
	}
}
int main()
{
	cin>>n>>m>>q>>u>>v>>t;
	p=(double)u/v;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		s.push(a[i]); 
	}
	work();
	int h=0;
	for(int i=t,l=1;l<=floor(m/t);i+=t,l++)
	{
		cout<<ans1[i]<<" ";
		if(h==m/t) break;
	}
	if(h==0) cout<<" ";
	cout<<endl;
	int ss=s.size();
	for(int i=1;i<=ss;i++)
	{
		ans2[i]=s.top();
		ans2[i]+=add;
		s.pop();
	}
	h=0;
	for(int i=t,l=1;l<=((m+n)/t);i+=t)
	{
		h++;
		l++;
		cout<<ans2[i]<<" ";
		if(h==( (n+m)/t )) break;
 	}
 	if(h==0) cout<<" ";
 	

	return 0;
}

.
.
.
.
说一下正解吧,(我太懒.)套用一下dalao的题解
在这里插入图片描述
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
using namespace std;
long long n,m,q,u,v,t,mm,b,mmm=0;
long long s1[20000000],s2[20000000],s3[20000000],f1=0,l1=0,f2=0,l2=0,f3=0,l3=0,add=0;
double p;
long long ans2[20000000],sum=0;
void work()
{
	long long a1,a2,ans1,ans2;
	f2=f3=1;
	for(int i=1;i<=m;i++)
	{	
		if(s1[f1]>=s2[f2] && s1[f1]>=s3[f3])
		{
			ans1=s1[f1];
			f1++;
		}
		else if(s2[f2]>=s1[f1] && s2[f2]>=s3[f3])
		{
			ans1=s2[f2];
			f2++;
		}
		else if(s3[f3]>=s1[f1] && s3[f3]>=s2[f2])
		{
			ans1=s3[f3];
			f3++;
		}
		ans1+=add;
		add+=q;
		a1=floor(p*(double)ans1);
		a2=ans1-a1;
		if(a1<a2) swap(a1,a2);
		a1-=add;
		a2-=add;
		l2++;
		l3++;
		s2[l2]=a1;
		s3[l3]=a2;
		
		
		if(i%t==0&&mmm<mm)
		{
			mmm++;
			cout<<ans1<<" ";
		}
	}
}
bool come(long long x,long long y)
{
	return x>y;
}
int main()
{
	for(int i=0;i<=10000000;i++)
	{
		s1[i]=s2[i]=s3[i]=-999999999999;
	}
	scanf("%lld %lld %lld %lld %lld %lld",&n,&m,&q,&u,&v,&t);
	p=(double)u/v;
	mm=floor(m/t);
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%lld",&s1[i]);
	}
	f1=1,l1=n;
	sort(s1+1,s1+n+1,come);
	work();
	if(mm=0) cout<<" ";
	cout<<endl;
	for(int i=f1;i<=l1;i++)
	{
		sum++;
		ans2[sum]=s1[i];
	}
	for(int i=f2;i<=l2;i++)
	{
		sum++;
		ans2[sum]=s2[i];
	}
	for(int i=f3;i<=l3;i++)
	{
		sum++;
		ans2[sum]=s3[i];
	}
	sort(ans2+1,ans2+sum+1,come);
	int h=0;
	for(int i=t,l=0;;i+=t)
	{
		h++;
		l++;
		cout<<ans2[i]+add<<" ";
		if(l==floor((n+m)/t)) break;
	}
	if(h==0) cout<<" ";
	return 0; 
}

.
.
.
.

T3-- 愤怒的小鸟

在这里插入图片描述
在这里插入图片描述
输入输出样例
输入样例#1: 复制
2
2 0
1.00 3.00
3.00 3.00
5 2
1.00 5.00
2.00 8.00
3.00 9.00
4.00 8.00
5.00 5.00
.
.
.

输出样例#1:
1
1
输入样例#2:
3
2 0
1.41 2.00
1.73 3.00
3 0
1.11 1.41
2.34 1.79
2.98 1.49
5 0
2.72 2.72
2.72 3.14
3.14 2.72
3.14 3.14
5.00 5.00
输出样例#2:
2
2
3
输入样例#3:
1
10 0
7.16 6.28
2.02 0.38
8.33 7.78
7.68 2.09
7.46 7.86
5.77 7.44
8.24 6.72
4.42 5.11
5.42 7.79
8.15 4.99
输出样例#3:
6

.
.
.
在这里插入图片描述
在这里插入图片描述

好了,一开始的想法还是暴力。
光是求a,b的公式我就推了巨久…
然后我发现到结束时候我连暴力的码不出来啊。。。。
直接说正解吧:

对于第i只猪,如果它已经被前面所构造的某条抛物线经过了,
就不用处理了,继续往下搜索。否则,就有两种选择,第一种
是与前面某一只单独的猪(即这只猪未与其它猪组成抛物线)
组成抛物线,因为两只猪最多也只能被一条抛物线相连;第二
种是暂时不与其它单独的猪组成抛物线。最后,将抛物线的条
数与余下的猪的个数相加(因为单独的一只猪也要一条抛物线
将其击中)就是搜索出来的一个合法的结果。

一个很重要的点,是因为浮点数的精度计算太过复杂,像3.14这样的数存在浮
点型变量里存的可能是3.139999999,也有可能是3.140000001,
所以不能直接用“==”判断两个浮点数是否相等。在这种情况
下,就允许判断两个浮点数为相等时,两数之间存在微小的误差,
这个“微小的误差”要取一个较小的数,比如1e-8,这样就不
会判不出也不会误判了!!!

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define LL long long
#define LD long double
using namespace std;
LD eps = 1e-8;
int n=0,m=0,ans=0;
LD x[25],y[25],pwxa[25],pwxb[25],tx[25],ty[25];
//x数组存每只猪的x坐标,y数组存每只猪的y坐标
//pwxa数组存每条已构造的抛物线的参数a,pwxb数组存每条已构造的抛物线的参数b
//tx数组存每只单独的猪的x坐标,ty数组存每只单独的猪的y坐标 
int t;
bool pd(LD x,LD y)//判断两个浮点数是否相等
{
	
	//如果a和b之间相差的值小于eps(即1e-8),就说明它们是相等的 
	return fabs(x-y)<eps;//fabs用于取浮点数的绝对值 
}
void dfs(int c,int u,int v)
//dfs(c,u,v)表示当前搜到第c只猪,已构造抛物线的数量为u,单独的猪的数量为v时的情况 
//注意:当前抛物线的总数量为(u+v)条,因为除已有的抛物线外,每一只单独的猪也需要一条抛物线来击中它 
{
	if(u+v>=ans) return ;//最优性剪枝,因为即使后面的每一只猪都被当前已构造的抛物线击中, 
    //或者与其它单独的猪组成抛物线,抛物线的总数量还是(u+v),不会减少,
    //所以如果抛物线的总数量已经大于等于当前的最优解时,搜下去也不会
    //比当前更优了,就不用继续搜下去了。
	
	if(c>n)//搜完了 
	{
		ans=u+v;
		return ;
	}
	bool flag=false;//记录是否被已构造的抛物线经过 
	for(int i=1;i<=u;i++)
	{
		if(pd( pwxa[i]*x[c]*x[c] + pwxb[i]*x[c] ,y[c]) )
		{
			dfs(c+1,u,v);//被经过了就直接往下搜 
			flag=1;
			break;//既然当前猪已经被经过了,再继续判断也没有意义了
		}
	}
	if(flag==0)//如果没有被经过 
	{
		for(int i=1;i<=v;i++)//枚举单独的猪 
		{
			if(pd(x[c],tx[i])) continue; //如果当前猪与这一只单独的猪的x坐标相同
			//就说明它们不能组成一条
            //抛物线。因为若想使它们组成一条抛物线,弹弓的位置就必须是它们的正下
            //方,然后往上垂直发射,但是弹弓的位置固定在(0,0),而猪的x坐标又大于
            //0(题目数据范围),所以它们不能组成一条抛物线。如果不加这个判
            //断的话就会计算出一些奇奇怪怪的东西,影响后面的判断,所以还是加上为好。 
			LD a=(y[c]*tx[i]-ty[i]*x[c])/(x[c]*x[c]*tx[i]-tx[i]*tx[i]*x[c]);
			LD b=(y[c]-a*x[c]*x[c])/x[c];
			if(a<0)//如果这一个解是合法的,就说明当前猪与这一只单独的猪可以组成抛物线 
			{
				pwxa[u+1]=a;//记录抛物线的参数a 
				pwxb[u+1]=b;//记录抛物线的参数b 
				LD q=tx[i],w=ty[i];//将这一只单独的猪从数组中删去
				//因为它已经和当前猪组成抛物线,不再单独了 
				for(int j=i;j<v;j++)
				{
					tx[j]=tx[j+1];
					ty[j]=ty[j+1];
				}
				dfs(c+1,u+1,v-1);
				//继续往下搜
                //将这一只单独的猪放回数组(回溯) 
				for(int j=v;j>i;j--)
				{
					tx[j]=tx[j-1];
					ty[j]=ty[j-1];
				}
				tx[i]=q;
				ty[i]=w;
			}
		}
		//还有一种选择:暂时不与其它猪组成抛物线 
		tx[v+1]=x[c];
		ty[v+1]=y[c];
		dfs(c+1,u,v+1);//继续往下搜 
	}
}
int main()
{
//	freopen("angrybirds.in","r",stdin);
//	freopen("angrybirds.out","w",stdout);
	scanf("%d",&t);
	while(t)
	{
		t--;
		scanf("%d %d",&n,&m);
		for(int i=1;i<=n;i++)
		{
			cin>>x[i]>>y[i];
		}
		ans=100;//千万记得要初始化啊 
		dfs(1,0,0);
		printf("%d\n",ans);
	}
	return 0;
}

总结:
noip2016 day2做的本蒟蒻很慌啊,还有一周就要noip2018复赛了,继续加油吧。
祝NOIP2018 RP++!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值