【解题报告】NOIP2014
Day1
T1 生活大爆炸版石头剪刀布
思路
模拟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #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=a2+2ab+b2
移项,就得到了
2ab=(a+b)2−(a2+b2)
对于多个点,我们也可以推出高维的结论
和这个平方差的结论类似
然后我们就直接搜就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| #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 无线网路发射器选址
思路
这个的话,直接前前缀和+模拟不就好了吗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| #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 寻找道路
思路
本来一个是一个裸的单源最短路径
没想到还是穿了个内裤的
首先我们要直接或者间接与终点连通的,所以我们建一个反图,标记终点可以到哪些点
然后我们如果标记到起点的话,说明可以,反之不可以
如果可以的话,我们在一开始的时候因为也要建正图
所以从起点开始做一个单源最短路径就好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| #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 解方程
思路
大概就是依次尝试一下成不成立吧
我们我们在输入的时候用秦九昭算法来处理一下
我们考虑到,我们每一个系数都模一个质数这样就好了
为什么呢,实际上比较玄学
但是有一定的严谨性
我们可以对多个质数取模,这样就能大大降低错误的概率
然后算这个表达式的时候也是边乘边模就好了,记住,要模之前的那个数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #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; }
|