NOIP2014提高组复赛解题报告

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/TA201314/article/details/41254529

Day1

T1生活大爆炸版剪刀石头布:模拟,水;

T2联合权值:树形DP,水;

T3Flappy Birds:

这道题我当时算时间复杂度算错了,O(nm^2)的时间复杂度给算成O(nm)了,所以根本就没想优化,以后①算时间复杂度的时候要小心一点了

其实正解也是很简单的,只是在直译式DP的基础上做了一点小优化。

直译式DP:设f(i,j)为到达当前点的最小步数,则

f(i,j)->f(i-1,j+Y[i-1])

      ->f(i-1,j-k*X[i-1])+k,k>0且j-k*X[i-1]>0

完全背包式优化:

f(i,j)->f(i-1,j-X[i-1])+1

      ->f(i,j-X[i-1])+1

      ->f(i-1,j+Y[i-1])

(顺序不能颠倒)

“因为f(i,j)这个状态实际上可以由f(i,j-X[i-1])这个状态转移而来"——GTY     orz

②这种多步与一步等价的思想实际上是非常实用和需要我掌握的。

回来以后写的AC代码:

#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
typedef short hd;
char * ptr=(char *)malloc(1000000);
int f[10001][1001];
inline void in(hd &x){
	while(*ptr<'0'||*ptr>'9')++ptr;
	x=0;
	while(*ptr>47&&*ptr<58)x=x*10+*ptr++-'0';
}
int main(){
	
	hd P[10000],down[10001],up[10001],X[10000],Y[10000],N,M,K,tmp,i,j;
	int MAXN=2000000000;
	
	freopen("birda.in","r",stdin);
	freopen("birda.out","w",stdout);
	
	//读入 
	fread(ptr,1,1000000,stdin);
	in(N),in(M),in(K);
	for(i=0;i<N;++i)in(X[i]),in(Y[i]);
	for(i=0;i<=N;++i)up[i]=M+1;
	for(i=0;i<K;++i)in(P[i]),in(down[P[i]]),in(up[P[i]]);
	//初始化 
	sort(P,P+K);
	memset(*f+1001,127,sizeof(f)-sizeof(int)*1001);
	f[0][0]=MAXN;
	//DP
	for(i=1;i<=N;++i){
		for(j=1;tmp=min((hd)(j+X[i-1]),M),j<=M;++j)f[i][tmp]=min(f[i][tmp],f[i-1][j]+1);
		for(j=1;tmp=min((hd)(j+X[i-1]),M),j<M;++j)f[i][tmp]=min(f[i][tmp],f[i][j]+1);
		for(j=M;j>Y[i-1];--j)f[i][j-Y[i-1]]=min(f[i][j-Y[i-1]],f[i-1][j]);
		for(j=0;j<=down[i];++j)f[i][j]=MAXN;
		for(j=up[i];j<=M;++j)f[i][j]=MAXN;
		
		//判断当前列是否可达
		j=1;
		while(j<=M&&f[i][j]>=MAXN)++j;
		if(j>M)break;
	}
	if(i>N)printf("1\n%d",*min_element(f[N]+1,f[N+1]));
	else printf("0\n%d",lower_bound(P,P+K,i)-P);
}


Day2

T1无线网络发射器选址:当时蛋疼写了个DP,实际上暴力就可以,水。

T2寻找道路:我发现D2好像一直很蛋疼,又写了个DFS+SPFA,实际上DFS+BFS就可以了。

T3:解方程:

这是一道很奇怪的题,我刚看到他的时候没有一点思路。。除了暴力。然后就想写个50分的压位高精暴力,结果还写残了。。所以只得了30分。回来的路上听到学长们讨论各种取模+乱搞。直接取模+暴力的话,O(nm)的时间复杂度实际上是可以得70分的。

那么就来说一下正确解法吧。——来自,各种大神的题解。

引理:若f(x)≡0(mod p),则f(x+p)≡0(mod p).

所以,我们可以先取一个小质数P,求出[1,P)内所有的解,然后可以通过+P推得在[1,m]内所有符合f(x)≡0(mod P),再用另一个大质数p,用来check每个解即可。

时间复杂度的话,应该是难以估计的,因为模质数本身就是一个用正确率换时间的做法嘛。。对于普通一点的数据的话,应该是可以达到O(n*p+n*n*m/p)。我们发现这玩意儿竟然是我刚学的对勾函数。。当p最接近(n*m)^(1/2)时,时间复杂度最低。此时O(10^2*10^4+10^4*10^6/10^4)≈O(2*10^6).

代码:

#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<cmath> 
#include<algorithm>
int main(){
	const int P=10007,p=100000009;
	int i,j,m,n,a[101]={0},b[101]={0},mi[101],ans[101],tmp,lst[10008],h=0,t=0;
	ans[0]=1,mi[0]=1;
	
	freopen("equationa.in","r",stdin);
	freopen("equationa.out","w",stdout);
	
	char * ptr=(char *)malloc(2000000);
	scanf("%d%d",&n,&m);
	fread(ptr,1,2000000,stdin);
	for(i=0;i<=n;++i){
		bool flag=0;
		while(*ptr<'0'||*ptr>'9')
			if(*ptr++=='-')
				flag=1;
		while(*ptr>47&&*ptr<58){
			a[i]=(a[i]*10+*ptr-'0')%P;
			b[i]=(b[i]*10+*ptr++-'0')%p;
		}
		if(flag){
			a[i]=-a[i];
			b[i]=-b[i];
		}
	}
	for(i=1;i<P;++i){
		
		for(j=1;j<=n;++j)mi[j]=((long long)mi[j-1]*i)%P;
		
		tmp=0;
		for(j=0;j<=n;++j)tmp=(tmp+(long long)mi[j]*a[j])%P;
		
		if(!tmp)lst[++t]=i;
	}
	do{
		h%=P;
		//check 
		for(j=1;j<=n;++j)mi[j]=((long long)mi[j-1]*lst[h])%p;
		tmp=0;
		for(j=0;j<=n;++j)tmp=(tmp+(long long)mi[j]*b[j])%p;
		if(!tmp)ans[ans[0]++]=lst[h];
		//扩展新节点 
		if(lst[h]+P<=m)lst[t=(t+1)%P]=lst[h]+P;
	}while(h++!=t);
	printf("%d",ans[0]-1);
	for(i=1;i<ans[0];++i)printf("\n%d",ans[i]);
}



③这道题应该说让我对hash有了全新的认识。其优化暴力的方法也是很巧妙的,将其花费n*p的时间复杂度模一个小质数之后,我们会发现我们可以判定在(p,m]的范围内有些值是必定不可取的了,即若f(x)≠0(mod p),f(x+p)≠0(mod p),这样便达到了对暴力的优化。

阅读更多
想对作者说点什么?
相关热词

博主推荐

换一批

没有更多推荐了,返回首页