• NOIP 2014


      • Prob.1 生活大爆炸版 石头剪刀布

    模拟。
    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    const int mp[5][5]={{0,0,1,1,0},
    					{1,0,0,1,0},
    					{0,1,0,0,1},
    					{0,0,1,0,1},
    					{1,1,0,0,0}};
    int A[205],B[205];
    int n,lA,lB,ansA,ansB;
    int main(){
    	scanf("%d%d%d",&n,&lA,&lB);
    	for(int i=1;i<=lA;i++) scanf("%d",&A[i]);
    	for(int i=1;i<=lB;i++) scanf("%d",&B[i]);
    	for(int i=1,pA=0,pB=0;i<=n;i++) {
    		pA++; if(pA>lA) pA=1;
    		pB++; if(pB>lB) pB=1;
    		ansA+=mp[A[pA]][B[pB]];
    		ansB+=mp[B[pB]][A[pA]];
    	}
    	printf("%d %d",ansA,ansB);
    	return 0;
    }
    

      • Prob.2 联合权值

    可以叫树形dp吧。
    每次考虑把当前子树的根作为长度为2的路径的一个端点或中转点。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define MAXN 200005
    using namespace std;
    const int mod=10007;
    struct edge{
    	int to,next;
    }e[MAXN*2];
    int w[MAXN],head[MAXN];
    int sm[MAXN],mx[MAXN];
    int n,ent=2,ansx,anss;
    void add(int u,int v){
    	e[ent]=(edge){v,head[u]};
    	head[u]=ent++;
    }
    void dfs(int u,int fa){
    	for(int i=head[u];i;i=e[i].next){
    		int v=e[i].to;
    		if(v==fa) continue;
    		dfs(v,u);
    		anss=(1ll*anss+1ll*sm[v]*w[u]*2)%mod;
    		anss=(1ll*anss+1ll*sm[u]*w[v]*2)%mod;
    		sm[u]+=w[v];
    		ansx=max(ansx,mx[v]*w[u]);
    		ansx=max(ansx,mx[u]*w[v]);
    		mx[u]=max(mx[u],w[v]);
    	}
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1,u,v;i<n;i++){
    		scanf("%d%d",&u,&v);
    		add(u,v); add(v,u);
    	}
    	for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    	dfs(1,0);
    	printf("%d %d",ansx,anss);
    	return 0;
    }
    

      • Prob.3 飞扬的小鸟

    dp+转移优化
    首先 70%的很好想:
    dp[i][j]表示到达(i,j)的最小点击次数,显然没有后效性。
                 ┌dp[i-1][j+y[i-1]] (不点击)
    dp[i][j]=min│
                 ┕dp[i-1][j-k*x[i-1]]+k(点击)

    这个方程的复杂度为 (n*m*m)
    考虑优化,发现这样一个东西:
    只考虑"点击"这一个转移来源的话,那么
    dp[i][j]只比dp[i][j-x[i-1]]多了一个转移来源。

    然后就是一个前缀的思想了,即我们把第二维的j从小到大枚举
    那么(这里在说"点击"这一个来源)
    dp[i][j]=min(dp[i-1][j-x[i-1]],dp[i][j-x[i-1])+1
    这样就把"点击"时的O(n)转移优化为了O(1)。

    注意j==m时要特殊处理。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define INF 0x3f3f3f3f
    using namespace std;
    int x[10005],y[10005],l[10005],h[10005],fg[10005];
    int dp[10005][1005];
    int n,m,k,ans=INF;
    bool legal(int i,int j){
    	if(j<=0||j>m) return 0;
    	if(!fg[i]) return 1;
    	return l[i]<j&&j<h[i];
    }
    void cmin(int &a,int b){
    	if(a>b) a=b;
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&k);
    	memset(dp,0x3f,sizeof(dp));
    	for(int i=0;i<n;i++) scanf("%d%d",&x[i],&y[i]);
    	for(int i=1,p;i<=k;i++) scanf("%d",&p),fg[p]=1,scanf("%d%d",&l[p],&h[p]);
    	for(int j=l[0]+1;j<(fg[0]?h[0]:m+1);j++) dp[0][j]=0;
    	for(int i=1;i<=n;i++){
    		//click
    		for(int j=0;j<m;j++){
    			int a=legal(i-1,j-x[i-1])?dp[i-1][j-x[i-1]]:INF;
    			int b=j>x[i-1]?dp[i][j-x[i-1]]:INF;
    			cmin(dp[i][j],min(a,b)+1);
    		}
    		for(int j=m;j>=0;j--){
    			int a=legal(i-1,j)?dp[i-1][j]:INF;
    			cmin(dp[i][m],a+max(m-j-1,0)/x[i-1]+1);
    		}
    		//no-click
    		for(int j=0;j<=m;j++){
    			int a=legal(i-1,j+y[i-1])?dp[i-1][j+y[i-1]]:INF;
    			cmin(dp[i][j],a);
    		}
    	}
    	for(int i=n;i>=0;i--){
    		for(int j=1;j<=m;j++){
    			if(i==n) cmin(ans,dp[i][j]);
    			if(i!=n&&legal(i,j)&&dp[i][j]<INF){
    				printf("%d
    %d",0,k);
    				return 0;
    			}
    		}
    		if(ans<INF) break;
    		if(fg[i]) k--;
    	}
    	printf("%d
    %d",1,ans);
    	return 0;
    }
    

      • Prob.4 无线网络发射器选址

    二维前缀和,求子矩阵和。
    千万不要枚举多了。否则会多统计。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    int sum[150][150];
    int d,n,ans,cnt;
    int area(int x1,int y1,int x2,int y2){
    	return sum[x2][y2]-sum[x1][y2]-sum[x2][y1]+sum[x1][y1];
    }
    int main(){
    	scanf("%d%d",&d,&n);
    	for(int i=1,x,y,k;i<=n;i++){
    		scanf("%d%d%d",&x,&y,&k);
    		x++;y++;
    		sum[x][y]+=k;
    	}
    	for(int i=1;i<=129;i++)
    		for(int j=1;j<=129;j++)
    			sum[i][j]=sum[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
    	for(int i=1,x1,y1,x2,y2,now;i<=129;i++)
    		for(int j=1;j<=129;j++){
    			x1=max(i-d-1,0); y1=max(j-d-1,0);
    			x2=min(i+d,129); y2=min(j+d,129);
    			now=area(x1,y1,x2,y2);
    			if(now>ans) ans=now,cnt=0;
    			if(now==ans) cnt++;
    		}
    	printf("%d %d",cnt,ans);
    	return 0;
    }
    

      • Prob.5 寻找道路

    反向BFS给无法到终点的点打上标记,
    然后再给那些存在一条出边无法到终点的点打上标记。
    最后对没有标记的点跑最短路。(因为边权为1,BFS即可)

    代码:

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    struct edge{
    	int to,next;
    }e1[200005],e2[200005];
    int head1[10005],head2[10005],vis[10005],dis[10005];
    int n,m,ent1=2,ent2=2,S,T; 
    void add(int u,int v,int &ent,int *head,edge *e){
    	e[ent]=(edge){v,head[u]};
    	head[u]=ent++;
    }
    void BFS1(){
    	queue<int> q;
    	vis[T]=1; q.push(T);
    	while(!q.empty()){
    		int u=q.front();q.pop();
    		for(int i=head2[u];i;i=e2[i].next){
    			int v=e2[i].to;
    			if(vis[v]) continue;
    			vis[v]=1; q.push(v);
    		}
    	}
    	for(int u=1;u<=n;u++) if(!vis[u])
    		for(int i=head2[u];i;i=e2[i].next){
    			int v=e2[i].to;
    			if(!vis[v]) continue;
    			vis[v]=-1;
    		}
    }
    void BFS2(){
    	queue<int>q; q.push(S); dis[S]=1;
    	while(!q.empty()){
    		int u=q.front();q.pop();
    		for(int i=head1[u];i;i=e1[i].next){
    			int v=e1[i].to;
    			if(dis[v]||vis[v]!=1) continue;
    			dis[v]=dis[u]+1;
    			q.push(v);
    		}
    	}
    	if(dis[T]==0) printf("-1");
    	else printf("%d",dis[T]-1);
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1,u,v;i<=m;i++)
    		scanf("%d%d",&u,&v),
    		add(u,v,ent1,head1,e1),
    		add(v,u,ent2,head2,e2);
    	scanf("%d%d",&S,&T);
    	BFS1();
    	BFS2();
    	return 0;
    }
    

      • Prob.6 解方程

    又是一大神题,车大佬给我讲了好久才明白。
    首先按照题目的意思,我们找出x,要使得:
    A0 + A1*X1 + A2*X2 + A3*X3 + ...... + An*Xn == 0  [1]
    但是无奈 A的范围很大,无法直接计算。
    (然后就阴差阳错的考虑到取模上去)
    对等式两边同时取模 M(先只对系数取模)
    那么得到:(Bi = Ai % M)
    B0 + B1*X1 + B2*X2 + B3*X3 + ...... + Bn*Xn == 0  [2]
    然后动动脑子,可以发现满足 [1]式的x 则是一定满足 [2]式的x。

    !!!
    那么我们是不是可以把原来的 [1]式 通过取模后得到系数较小的 [2]式,
    然后通过 可以计算的[2]式来找合法的x。

    好吧,显然有错。
    虽然满足 [1]式的x  一定是满足 [2]式的x,
    但是满足 [2]式的x  却不一定是满足[1]式的x。(这个好理解吧)
    显然满足 [2]式的x,带入[1]式左边,可能得到两个值,一个是0(合法),一个是k * M(非法)

    那怎么办? 接下来就神奇了。
    我们多选几个 M(5-6个) : M1 , M2 , M3 ,M4 , M5(M尽量为大素数或某些大素数的乘积
    那么对于一个x,在这5种M取模下,如果都满足 [2]式(即其值==0),那么这个x就非常可能是满足 [1]式的,直接把它当做答案。

    为什么呢。
    如果都满足,那么把这个 x 带入 [1]式后,得到的值要么为 0(合法),要么为 k * M1M2M3M4M5(非法)。
    (对于非法的 x,就是带入 [1]式后,其值==k *  M1M2M3M4M5,即是"我们选取的这几个模值的乘积的倍数"。)
    而对于所有的x,带入 [1]式后,最多产生 10^6个值。
    那么在出题人不知道我们选取的模值的情况下,要使得某个x带入 [1]式后恰好等于"我们选取的模数的乘积的倍数"的几率是很小的。

    所以,除非运气不好,否则我们选的几个M (强调:M尽量为大素数或某些大素数的乘积),就可以达到筛选出正确的x的目的。

    #include<cctype>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define _ %mod[j] 
    using namespace std;
    const int mod[6]={13923, 16851, 24989, 19997, 11987, 13999};
    int val[1000005][6]/*mod意义下*/,A[105][6],ANS[1000005];
    int n,m,cnt;
    void readai(){
    	int fu;char ch; ch=getchar();
    	for(int i=0;i<=n;i++){
    		fu=1;
    		while(!isdigit(ch)){
    			if(ch=='-') fu=-1;
    			ch=getchar();
    		}
    		while(isdigit(ch)){
    			for(int j=0;j<=5;j++)
    				A[i][j]=(A[i][j]*10+(ch-'0'))_;
    			ch=getchar();
    		}
    		for(int j=0;j<=5;j++) A[i][j]*=fu;
    	}
    }
    int Pow(int a,int b,int j){
    	int now=1;
    	while(b){
    		if(b&1) now=(1ll*now*a)_;
    		a=(1ll*a*a)_;
    		b>>=1;
    	}
    	return now;
    }
    int cal(int x,int j){
    	int ans=0;
    	for(int i=0;i<=n;i++)
    		ans=(ans+(1ll*A[i][j]*Pow(x,i,j))_)_;
    	return ans; 
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	readai();
    	for(int j=0;j<=5;j++)
    		for(int i=1;i<mod[j];i++)
    			val[i][j]=cal(i,j);
    	for(int i=1;i<=m;i++){
    		bool fg=1;
    		for(int j=0;j<=5;j++)
    			if(val[(i)_][j]) fg=0;
    		if(fg) ANS[++cnt]=i;
    	}
    	printf("%d
    ",cnt);
    	for(int i=1;i<cnt;i++) printf("%d
    ",ANS[i]);
    	printf("%d",ANS[cnt]);
    	return 0;
    }
    
  • 相关阅读:
    IO多路复用
    Elasticsearch之打分机制、集群搭建、脑裂问题
    Elasticsearch之-映射管理,ik分词器
    Elasticsearch之高亮查询,聚合查询
    Elasticsearch之索引、文档、组合查询、排序查询、filter过滤操作
    Go 数组、切片、Maps
    Go if-else语句、for循环、switch语句
    Go 函数、包、mode模式
    毕业设计-1.05
    毕业设计-1.04
  • 原文地址:https://www.cnblogs.com/zj75211/p/7799999.html
Copyright © 2020-2023  润新知