zoukankan      html  css  js  c++  java
  • 【解题报告】NOIP2014

    【解题报告】NOIP2014

    Day1

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

    思路

    模拟

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    using namespace std;
    int n,na,nb;
    int a[205],b[205];
    int sa,sb;
    int score[10][10]={{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 main()
    {
    	cin>>n>>na>>nb;
    	for(int i=0;i<na;i++)
    	cin>>a[i];
    	for(int i=0;i<nb;i++)
    	cin>>b[i];
    	for(int i=0;i<n;i++)
    	{
    		sa+=score[a[i%na]][b[i%nb]];
    		sb+=score[b[i%nb]][a[i%na]];
    	}
    	cout<<sa<<" "<<sb<<endl;
    	return 0;
    }
    

    T2 联合权值

    思路

    我们可以发现,我们对于每个点可以枚举两个出点,这样距离就为2,然后我们枚举找到两个权值最大的点,这两个点产生的联合权值就最大

    那么这两个点的联合权值怎么算呢

    我们可以使用平方和公式

    \[(a+b)^2=a^2+2ab+b^2 \]

    移项,就得到了

    \[2ab=(a+b)^2-(a^2+b^2) \]

    对于多个点,我们也可以推出高维的结论

    和这个平方差的结论类似

    然后我们就直接搜就可以了

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    using namespace std;
    const int mod=10007;
    const int maxn=200005;
    struct edge{
    	int e,next;
    }ed[maxn*2];
    int en,first[maxn];
    void add_edge(int s,int e)
    {
    	en++;
    	ed[en].next=first[s];
    	first[s]=en;
    	ed[en].e=e;
    }
    int n;
    int val[maxn];
    int ans1,ans2;
    int main()
    {
    	int n;
    	cin>>n;
    	for(int i=1;i<=n-1;i++)
    	{
    		int u,v;
    		cin>>u>>v;
    		add_edge(u,v);
    		add_edge(v,u);
    	}
    	for(int i=1;i<=n;i++)
    	cin>>val[i];
    	for(int i=1;i<=n;i++)
    	{
    		int mx1=-99999,mx2=-99999;
    		int sq=0,sum=0;
    		for(int j=first[i];j;j=ed[j].next)
    		{
    			int e=ed[j].e;
    			if(mx1<val[e])
    			{
    				mx2=mx1;
    				mx1=val[e];
    			}
    			else if(mx2<val[e])
    			{
    				mx2=val[e];
    			}
    			sum=(sum+val[e])%mod;
    			sq=(sq+val[e]*val[e]%mod);
    		}
    		sum=sum*sum%mod;
    		ans2=(ans2+sum-sq+mod)%mod;
    		ans1=max(ans1,mx1*mx2);
    	}
    	cout<<ans1<<" "<<ans2<<'\n';
    	return 0; 
    }
    

    T3 飞扬的小鸟

    思路

    动态规划,一个背包类的模板

    \(f[i][j]\) 表示横坐标为 \(i\) 的时候,高度为 \(j\) ,这个时候最小点击次数是多少

    上升的时候呢,就是一个完全背包

    下降的时候就是01背包

    如果纵坐标超过 \(m\) 的话,那我们就直接把纵坐标变成 \(m\)

    From:https://www.luogu.com.cn/blog/JOE/solution-p1941

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    using namespace std;
    const int maxn=10005;
    const int INF=0x3f3f3f; 
    int n,m,k;
    int x[maxn],y[maxn];
    int down[maxn],up[maxn];
    int f[maxn][2005];
    bool flag[maxn];
    int main()
    {
    	cin>>n>>m>>k;
    	for(int i=1;i<=n;i++)
    	{
    		cin>>x[i]>>y[i];
    		up[i]=m;
    		down[i]=1;
    	}
    	for(int i=1;i<=k;i++)
    	{
    		int a,b,c;
    		cin>>a>>b>>c;
    		flag[a]=1;
    		down[a]=b+1;
    		up[a]=c-1;
    	}
    	memset(f,0x3f3f3f,sizeof(f));
    	for(int i=1;i<=m;i++)
    	f[0][i]=0;
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=x[i]+1;j<=x[i]+m;j++)
    			f[i][j]=min(f[i-1][j-x[i]]+1,f[i][j-x[i]]+1);
    		for(int j=m+1;j<=x[i]+m;j++)
    			f[i][m]=min(f[i][j],f[i][m]);
    		for(int j=1;j<=m-y[i];j++)
    			f[i][j]=min(f[i][j],f[i-1][j+y[i]]);
    		for(int j=1;j<down[i];j++)
    			f[i][j]=INF;
    		for(int j=up[i]+1;j<=m;j++)
    			f[i][j]=INF; 
    	}
    	int ans=INF;
    	for(int i=1;i<=m;i++)
    	ans=min(ans,f[n][i]);
    	if(ans<INF)
    	{
    		cout<<1<<'\n'<<ans<<'\n';
    		return 0;
    	}
    	int i,j;
    	for(i=n;i>=1;i--)
    	{
    		for(j=1;j<=m;j++)
    		if(f[i][j]<INF)
    		break;
    		if(j<=m)
    		break;
    	}
    	ans=0;
    	for(j=1;j<=i;j++)
    	if(flag[j])
    	ans++;
    	cout<<0<<'\n'<<ans<<'\n';
    	return 0;
    }
    

    Day2

    T1 无线网路发射器选址

    思路

    这个的话,直接前前缀和+模拟不就好了吗

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string>
    #include <cstring>
    using namespace std;
    const int maxn=150;
    int d,n;
    int m[maxn][maxn];
    long long ans1=-999999,ans2;
    int main()
    {
    	cin>>d>>n;
    	for(int i=1;i<=n;i++)
    	{
    		int x,y,k;
    		cin>>x>>y>>k;
    		m[x][y]=k;
    	}
    	long long mx=0;
    	for(int i=0;i<=128;i++)
    	{
    		for(int j=0;j<=128;j++)
    		{
    			int x1,x2,y1,y2;
    			mx=0;
    			if(i-d<0)
    			x1=0;
    			else
    			x1=i-d;
    			if(i+d>128)
    			x2=128;
    			else
    			x2=i+d;
    			if(j-d<0)
    			y1=0;
    			else
    			y1=j-d;
    			if(j+d>128)
    			y2=128;
    			else
    			y2=j+d;
    			for(int a=x1;a<=x2;a++)
    			{
    				for(int b=y1;b<=y2;b++)
    				mx+=m[a][b];
    			}
    			if(mx>ans1)
    			ans1=mx,ans2=1;
    			else if(mx==ans1)
    			ans2++;
    			else
    			continue;
    		}
    	}
    	cout<<ans2<<" "<<ans1<<'\n';
    	return 0;
    }
    

    T2 寻找道路

    思路

    本来一个是一个裸的单源最短路径

    没想到还是穿了个内裤的

    首先我们要直接或者间接与终点连通的,所以我们建一个反图,标记终点可以到哪些点

    然后我们如果标记到起点的话,说明可以,反之不可以

    如果可以的话,我们在一开始的时候因为也要建正图

    所以从起点开始做一个单源最短路径就好了

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #include <queue>
    using namespace std;
    const int maxn=300005;
    struct edge{
    	int e,next;
    }ed[maxn*2];
    int en,first[maxn];
    void add_edge(int s,int e)
    {
    	en++;
    	ed[en].next=first[s];
    	first[s]=en;
    	ed[en].e=e;
    }
    int n,m,s,t;
    int d[maxn];
    bool qyj[maxn],qy[maxn],yj[maxn];
    void spfa(int s)
    {
    	memset(qyj,false,sizeof(qyj));
    	memset(d,0x3f3f3f,sizeof(d));
    	queue <int> q;
    	q.push(s);
    	qyj[s]=true;
    	d[s]=0;
    	while(q.size()) 
    	{
    		int x=q.front();
    		q.pop();
    		qyj[s]=false;
    		for(int i=first[x];i;i=ed[i].next)
    		{
    			int e=ed[i].e;
    			if(d[e]>d[x]+1&&qy[e])
    			{
    				d[e]=d[x]+1;
    				if(!qyj[e])
    				{
    					qyj[e]=true;
    					q.push(e);
    				}
    			}
    		}
    	}
    }
    void bfs(int t)
    {
    	memset(qyj,false,sizeof(qyj));
    	queue <int> q;
    	q.push(t);
    	qy[t]=qyj[t]=true;
    	while(q.size())
    	{
    		int x=q.front();
    		q.pop();
    		for(int i=first[x];i;i=ed[i].next)
    		{
    			int e=ed[i].e;
    			if(!qyj[e])
    			{
    				qyj[e]=true;
    				qy[e]=true;
    				q.push(e);
    			}
    		}
    	}
    }
    int main()
    {
    	cin>>n>>m;
    	for(int i=1;i<=m;i++)
    	{
    		int x,y;
    		cin>>x>>y;
    		add_edge(x,y);
    	}
    	cin>>s>>t;
    	bfs(t);
    	for(int i=1;i<=n;i++)
    	yj[i]=qy[i];
    	for(int i=1;i<=n;i++)
    	{
    		if(!yj[i])
    		{
    			for(int j=first[i];j;j=ed[j].next)
    			{
    				int e=ed[j].e;
    				if(yj[e])
    				yj[e]=false;
    			}
    		}
    	}
    	spfa(t);
    	if(d[s]>=0x3f3f3f)
    	cout<<-1<<'\n';
    	else
    	cout<<d[s]<<'\n';
    	return 0;
    }
    
    

    T3 解方程

    思路

    大概就是依次尝试一下成不成立吧

    我们我们在输入的时候用秦九昭算法来处理一下

    我们考虑到,我们每一个系数都模一个质数这样就好了

    为什么呢,实际上比较玄学

    但是有一定的严谨性

    我们可以对多个质数取模,这样就能大大降低错误的概率

    然后算这个表达式的时候也是边乘边模就好了,记住,要模之前的那个数字

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #define int long long
    using namespace std;
    const int p=1000000007;
    const int maxn=1005;
    int a[maxn],ans[1000003];
    int n,m;
    int en;
    bool t=true;
    inline int read()
    {
    	int x=0,f=1;
    	char c=getchar(); 
    	while(c<'0'||c>'9') 
    	{
    		if(c=='-')
    		f=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9')
    	{
    		x=((x<<1)+(x<<3)+c-'0')%p;
    		c=getchar();
    	}
    	return x*f;
    }
    int f(int x)
    {
    	int res=0;
    	for(int i=n;i>=1;i--)
    	res=((res+a[i])*x)%p;
    	res=(res+a[0])%p;
    	return res==0;
    }
    signed main()
    {
    	n=read(),m=read();
    	for(int i=0;i<=n;i++)
    	a[i]=read();
    	for(int i=1;i<=m;i++)
    	{
    		if(f(i))
    		{
    			t=false;
    			ans[0]++;
    			en++;ans[en]=i;
    		}
    	}
    	if(t)
    	{
    		cout<<ans[0]<<endl;
    		return 0;
    	}
    	cout<<ans[0]<<endl;
    	for(int i=1;i<=en;i++)
    	cout<<ans[i]<<endl;
    	return 0;
    }
    
    本博文为wweiyi原创,若想转载请联系作者,qq:2844938982
  • 相关阅读:
    POJ 2528 Mayor's posters(线段树)
    Codeforces Beta Round #22 (Div. 2 Only) C. System Administrator(构造割点)
    HDU 4417 Super Mario(划分树)
    Codeforces Beta Round #22 (Div. 2 Only) D. Segments(贪心)
    HDU 1247 Hat’s Words(字典树)
    HDU 3639 HawkandChicken(强连通分量)
    HDU 3394 Railway(点双连通分量)
    HDU 1394 Minimum Inversion Number(树状数组)
    HDU 3874 Necklace(树状数组+离线处理)
    树状数组
  • 原文地址:https://www.cnblogs.com/wweiyi2004/p/15545377.html
Copyright © 2011-2022 走看看