zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",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
    %d
    ",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
    ",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
    ",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
    ",second[0]);
    	for (int i = 1; i <= second[0]; ++i) printf("%d
    ", second[i]);
    	return 0;
    }
    

      

  • 相关阅读:
    书籍推荐:《C#7.0本质论》
    KPI在小型产品团队中的实践
    程序员如何学习英语
    端到端测试实践:Jenkins集成TestCafe
    对产品质量的一点思考
    不断进化的分支和需求管理
    书籍推荐:《More Effective C#》
    带你了解C#每个版本新特性
    不断进化的分支和需求管理
    怎样学习和阅读技术书籍?
  • 原文地址:https://www.cnblogs.com/xgtao984/p/5705580.html
Copyright © 2011-2022 走看看