NOIP 2014

DAY1

T1 生活大爆炸版石头剪刀布

 

一道简单的模拟题,给出对应的关系,要打表!打表!打表!

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;

//Golbel define
const int N = 210;
const int map[5][5] = {
	{ 0,-1, 1, 1,-1},
	{ 1, 0,-1, 1,-1},
	{-1, 1, 0,-1, 1},
	{-1,-1, 1, 0, 1},
	{ 1, 1,-1,-1, 0}
};
int afi[N],bfi[N];
int n,lena,lenb,nowa,nowb,scoa,scob;


int main(){
	freopen("rps.in","r",stdin);
	freopen("rps.out","w",stdout);
	scanf("%d",&n);
	scanf("%d%d",&lena,&lenb);
	for(int i = 0;i < lena;++i)scanf("%d",&afi[i]);
	for(int i = 0;i < lenb;++i)scanf("%d",&bfi[i]);
	for(int i = 1;i <= n;++i){
		if(nowa == lena)nowa = 0;
		if(nowb == lenb)nowb = 0;
		if(map[afi[nowa]][bfi[nowb]] == 1)scoa++;
		else if(map[afi[nowa]][bfi[nowb]] == -1)scob++;
		nowa++;
		nowb++;
	}
	printf("%d %d\n",scoa,scob);
	return 0;
}

  

T2 联合权值

 

求出距离为2的所有点的权值的乘积的和的两倍,枚举中点,直接算所有点的积,显然超时,那么计算所有点(a+b+c+d+.....+n)^2-(a^2-b^2+c^2+d^2+...+n^2)

 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;

//Golbel define 
const int N = 200010;
const int mod = 10007;
vector <int> edge[N];
int n,u,v,w[N],ans,maxi;
//end Golebel define

int main(){
	freopen("link.in","r",stdin);
	freopen("link.out","w",stdout);
	scanf("%d",&n);
	for(int i = 1;i < n;++i){
		scanf("%d%d",&u,&v);
		edge[u].push_back(v);
		edge[v].push_back(u);
	}
	for(int i = 1;i <= n;++i){
		scanf("%d",&w[i]);
	}
	for(int i = 1;i <= n;++i){
		int size = edge[i].size();
		int sum = 0,tot = 0,ret = 0;//sum = {a+b+c...+n}^2 tot = {a^2+b^2+c^2...+n^2}
		int maxfirst = 0,maxsecond = 0;
		for(int j = 0;j < size;++j){
			int v = edge[i][j];
			if(w[v]>maxfirst){
				maxsecond = maxfirst;
				maxfirst = w[v];
			}
			else if(w[v]>maxsecond){
				maxsecond = w[v];
			}
			sum = (sum%mod + w[v]%mod)%mod;
			tot = (tot%mod + (w[v]*w[v])%mod)%mod;
		}
		maxi = max(maxi,(maxfirst*maxsecond));
		ret = ( ( (sum*sum)%mod-tot%mod )%mod+mod)%mod;
		(ans += ret) %= mod;
	}
	printf("%d %d\n",maxi,ans);
	return 0;
}

  

T3 飞扬的小鸟

定义状态dp[i][j]为在(i,j)至少按的次数

总共有两种情况{

  一.上升:1.上升一步  dp[i][j] = min{dp[i-1][j-x[i-]]+1}

       2.上升多步  dp[i][j] = min{dp[i-1][j-k*x[i-1]]+k}这样是会超时的,因为有重叠子问题,那么先考虑飞的情况后考虑坠的情况,那么由下面的点转移而来

min{dp[i][j-x[i-1]]+1}。

      3.上升到图的最高点并且只一步dp[i][m] = min{dp[i-1][k]+1}(m-x[i-1]<=k<=m)

      4.上升到图的最高点多步的情况dp[i][m] = min(dp[i][k]+1)

  二.下降:dp[i][j] = min{dp[i-1][j+y[i-1]]}

}

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

//Golbel define
const int inf = 0x3fffffff;
const int M = 1010;
const int N = 10010;
int n,m,k;
int dp[N][M];
int fly[N],fal[N],up[N],down[N],x[N];
//end Golbel defien

int main(){
	freopen("bird.in","r",stdin);
	freopen("bird.out","w",stdout);	
	scanf("%d%d%d",&n,&m,&k);
	for(int i = 0;i < n;++i){
		scanf("%d%d",&fly[i],&fal[i]);
	}
	for(int i = 1;i <= n;++i){
		up[i] = m;
		down[i] = 1;
	}
	for(int i = 1;i <= k;++i){
		int l,h;
		scanf("%d%d%d",&x[i],&l,&h);
		down[x[i]] = l+1;up[x[i]] = h-1;
	}
	for(int i = 0;i <= n;++i){
		for(int j = 0;j <= m;++j){
			dp[i][j] = inf;
		}
	}
	for(int i = 1;i <= m;++i)dp[0][i] = 0;
	for(int i = 1;i <= n;++i){
		for(int j = 1;j <= m;++j){
			if(j > fly[i-1]){
				dp[i][j] = min(dp[i-1][j-fly[i-1]]+1,dp[i][j]);
				dp[i][j] = min(dp[i][j-fly[i-1]]+1,dp[i][j]);
			}
			if(j == m){
				for(int k = m-fly[i-1];k <= m;++k){
					dp[i][j] = min(dp[i][j],dp[i-1][k]+1);
					dp[i][j] = min(dp[i][j],dp[i][k]+1);
				}
			}
		}
		for(int j = down[i];j <= up[i];++j){
			if(j+fal[i-1] <= m){
				dp[i][j] = min(dp[i-1][j+fal[i-1]],dp[i][j]);
			}
		}
		for(int j = 0;j < down[i];++j)dp[i][j] = inf;
		for(int j = up[i]+1;j <= m;++j)dp[i][j] = inf;
	}
	int ans = inf;
	for(int i = 1;i <= m;++i)ans = min(ans,dp[n][i]);
	if(ans != inf){
		printf("1\n%d\n",ans);
	}
	else{
		puts("0");
		int cnt = 0,flag = false;
		for(int i = n-1;i >= 0;--i){
			if(flag)break;
			for(int j = down[i];j <= up[i];++j){
				if(dp[i][j] != inf){
					flag = true;
					cnt = i;
					break;
				}
			}
		}
		sort(x+1,x+k+1);
		for(int i = 1;i <= k;++i){
			if(cnt < x[i]){
				printf("%d\n",i-1);
				break;
			}
		}
	}
	return 0;
}

  

 DAY2

 

T1 无线网络发射器选址

 

暴力枚举每一个地址,暴力计算,注意边界,也可以用前缀和优化,也可以套二维树状数组。

 

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long

const int N = 200;
LL map[N][N],sum[N][N];

int main(){
	freopen("wireless.in","r",stdin);
	freopen("wireless.out","w",stdout);
	int n,d,x,y;
	LL k;
	scanf("%d%d",&d,&n);	
	for(int i = 1;i <= n;++i){
		scanf("%d%d%I64d",&x,&y,&k);
		map[x][y] = k;
	}
	LL ansi = 0,maxi = -1;
	for(int i = 0;i <= 128;++i){
		for(int j = 0;j <= 128;++j){
			LL cnt = 0;
			for(int a = max(0,i-d);a <= min(i+d,128);++a){
				for(int b = max(0,j-d);b <= min(j+d,128);++b){
					cnt += map[a][b];
				}
			}
			if(cnt > maxi){
				maxi = cnt;
				ansi = 1;
			}
			else if(cnt == maxi){
				ansi++;
			}
		}
	}
	cout<<ansi<<" "<<maxi<<endl;
	return 0;
}

  

 

T2 寻找道路

 

问一条特殊的最短路,特殊在于每一点所连的点都能够到达终点。从终点反向一次搜索把能够到达终点的点都标记下来,再正向一次枚举若标记的点可以到达没有标记的点,那么这个点就取消标记,最后一次Dijkstra最短路。

 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;

const int N = 10010;
const int M = 800010;
const int inf = 1<<30;
int n,m,x,y,s,t;
struct edge{
	int v,w;
	edge *nxt;
}*cur,*head[N],*back_head[N],meo[M];

struct node{
	int dis,u;
	bool operator < (const node &rhs)const{
		return dis >rhs.dis;
	}
};

int mark[N],done[N],dis[N],vis[N];

void adde(int u,int v,edge *f[]){
	cur->v = v;
	cur->w = 1;
	cur->nxt = f[u];
	f[u] = cur++;
}

void dfs(int x){
	mark[x] = 1;
	for(edge *it = back_head[x];it;it = it->nxt){
		int v = it->v;
		if(!mark[v])dfs(v);
	}
}

void Dijkstra(int s){
	priority_queue <node> q;
	for(int i = 1;i <= n;++i)dis[i] = inf;
	dis[s] = 0;
	q.push((node){dis[s],s});
	while(!q.empty()){
		node p = q.top();q.pop();
		int u = p.u;
		if(done[u])continue;
		done[u] = true;
		for(edge *it = head[u];it;it = it->nxt){
			int v = it->v;
			if(dis[v] > dis[u]+it->w && mark[v] == 1){
				dis[v] = dis[u]+it->w;
				q.push((node){dis[v],v});
			}
		}
	}
}

int main(){
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
	cur = meo;
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= m;++i){
		scanf("%d%d",&x,&y);
		adde(x,y,head);
		adde(y,x,back_head);
	}
	scanf("%d%d",&s,&t);
	memset(mark,0,sizeof(mark));
	dfs(t);
	for(int i = 1;i <= n;++i){
		if(mark[i]){
			for(edge *it = head[i];it;it = it->nxt){
				int v = it->v;
				if(!mark[v]){
					mark[i] = -1;//不能为0 
					break;
				}
			}
		}
		
	}
	Dijkstra(s);
	if(dis[t] == inf)puts("-1");
	else printf("%d\n",dis[t]);
	return 0;
}

  

T3 解方程

 

f(x) = a[0]+a[1]*x+a[2]*x^2+a[3]*x^3+......a[n]*x^n = 0;

既然f(x) = 0,0%p = 0,那么f(x)%p = 0,这里运用hash的思想,这是不对的想法但是错误的概率很小,类似的费马小定理的逆用是相似的,那么很容易想到的是一个O(n)枚举所有在范围里面的解,但是是要超时的,既然f(x)%p = 0,那么很显然f(x+p)%p = 0,那么就先预处理出,x在0~p-1的范围的数,大于其的数直接%p来判断方程是否为0。

类似于Hash要选择素数。

 

附加神犇vfk的想法:

我们可以选一个好取模的素数比如2^311=2147483647。

由于 a2^31+ba+b(mod2^311),所以 x 与 (x & 0x7fffffff) + (x >> 31) 是同余的!

register unsigned long long y = 0;
for (int i = n; i >= 0; i--)
{
    y = y * x + a[i];
    y = (y & 0x7fffffff) + (y >> 31);
}
y %= 0x7fffffff;

这样就跑得快了三倍了!(从1.2s变成了0.4s)

在此基础上再多加几个奇怪的素数模一模判一判就好了。只要不是零多项式那么至多只有n个根,所以后面不加什么常数优化也没关系。

 

 

献上我的代码: 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;

#define LL long long
const int N = 110;
const int mod1 = 38833;
const int mod2 = 998244353;
const int M = 1000010;
char s[10010];
int n,m,a[M],ans[M],cnt,b[M],first[M],second[M];

int  main(){
	freopen("equation.in","r",stdin);
	freopen("equation.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i = 0;i <= n;++i){
		scanf("%s",s);
		int x1 = 0,x2 = 0,flag = 1;
		if(s[0] == '-')flag = -1;
		else {
			x1 = s[0]-'0';
			x2 = s[0]-'0';
		}
		for(int i = 1;i < strlen(s);++i){
			x1 = (x1*10+s[i]-'0')%mod1;
			x2 = ((LL)x2*10+s[i]-'0')%mod2;
		}
		a[i] = x1,b[i] = x2;
		if(~flag){
			a[i] = -x1;
			b[i] = -x2;
		}
	}
	for(int i = 0;i < mod1;++i){
		ans[i] = a[n];
		for(int j = n-1;~j;--j)ans[i] = (ans[i]*i+a[j])%mod1; 
	}
	for(int i = 1;i <= m;++i){
		if(!ans[i%mod1])first[++(*first)] = i;//f(x+p)%p = 0 -> f(x)%p = 0
	}
	for(int i = 1;i <= *first;++i){
		int x = first[i];ans[i] = b[n];
		for(int j = n-1;~j;--j)ans[i] = ((LL)ans[i]*x+b[j])%mod2;
	}
	for(int i = 1;i <= *first;++i)if(!ans[i])second[++(*second)] = first[i];
	printf("%d\n",second[0]);
	for (int i = 1; i <= second[0]; ++i) printf("%d\n", second[i]);
	return 0;
}

  

posted @ 2016-07-26 08:55  xgtao984  阅读(244)  评论(0编辑  收藏  举报