首页 > 代码库 > noip2014 小结
noip2014 小结
D1T1 水水的模拟。。。
#include<iostream>#include<cstdio>#include<cstdlib>using namespace std;int a[500]={0},b[500]={0};int ju(int aa,int bb){ if (aa==0) { if (bb==0) return 0; if (bb==1) return -1; if (bb==2) return 1; if (bb==3) return 1; if (bb==4) return -1; } if (aa==1) { if (bb==0) return 1; if (bb==1) return 0; if (bb==2) return -1; if (bb==3) return 1; if (bb==4) return -1; } if (aa==2) { if (bb==0) return -1; if (bb==1) return 1; if (bb==2) return 0; if (bb==3) return -1; if (bb==4) return 1; } if (aa==3) { if (bb==0) return -1; if (bb==1) return -1; if (bb==2) return 1; if (bb==3) return 0; if (bb==4) return 1; } if (aa==4) { if (bb==0) return 1; if (bb==1) return 1; if (bb==2) return -1; if (bb==3) return -1; if (bb==4) return 0; }}int main(){ freopen("rps.in","r",stdin); freopen("rps.out","w",stdout); int ansa=0,ansb=0,na,nb,n,i,j,ca=0,cb=0,ans=0; scanf("%d%d%d",&n,&na,&nb); for (i=1;i<=na;++i) scanf("%d",&a[i]); for (i=1;i<=nb;++i) scanf("%d",&b[i]); ca=0;cb=0; for (i=1;i<=n;++i) { ca=ca%na+1; cb=cb%nb+1; ans=ju(a[ca],b[cb]); if (ans==1) ++ansa; if (ans==-1) ++ansb; } printf("%d %d\n",ansa,ansb); return(0); fclose(stdin); fclose(stdout);}
D1T2 水水的树形dp(可是考试的时候写残了,建错了树,以后一定要深搜建树。)
有一点小技巧:每次保存这个节点的孩子的价值之和、孩子中价值的最大值和次大值(求最大值时节省时间)。(fye大神写错了最大值和次大值的更新竟然A了。。。全省第8!!!)
还是有一点小问题,就是在求节点的孩子的价值之和的时候也要%10007,不然就会超过int
#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>using namespace std;struct use{ int va,maxi,maxii,sum;}tree[200001];int ans=0,maxn=0,next[400001]={0},point[200001]={0},en[400001]={0};bool visit[200001]={false};void work(int i){ int y,k,j=0; y=point[i]; while(y!=0) { if (!visit[en[y]]) { visit[en[y]]=true; work(en[y]); if (tree[en[y]].va>=tree[i].maxi) { tree[i].maxii=tree[i].maxi; tree[i].maxi=tree[en[y]].va; } else { if (tree[en[y]].va>tree[i].maxii&&tree[en[y]].va<tree[i].maxi) tree[i].maxii=tree[en[y]].va; } ans=(ans+(tree[i].sum*tree[en[y]].va)%10007)%10007; tree[i].sum=(tree[i].sum+tree[en[y]].va)%10007; ans=(ans+(tree[i].va*tree[en[y]].sum)%10007)%10007; if (tree[en[y]].maxi*tree[i].va>maxn) maxn=tree[en[y]].maxi*tree[i].va; if (tree[i].maxi*tree[i].maxii>maxn) maxn=tree[i].maxi*tree[i].maxii; } y=next[y]; }}int main(){ freopen("link.in","r",stdin); freopen("link.out","w",stdout); int n,i,j,u,v,tot=0; memset(tree,0,sizeof(0)); scanf("%d",&n); for (i=1;i<n;++i) { scanf("%d%d",&u,&v); ++tot; next[tot]=point[u]; point[u]=tot; en[tot]=v; ++tot; next[tot]=point[v]; point[v]=tot; en[tot]=u; } for (i=1;i<=n;++i) scanf("%d",&tree[i].va); visit[1]=true; work(1); ans=((ans%10007)*2)%10007; printf("%d %d\n",maxn,ans); return(0); fclose(stdin); fclose(stdout);}
D1T3 小小的dp(考试的时候写了nm^2的算法,竟然有wa,只得了60。关键是当时想到了完全背包的优化,敲了一遍又删了。。。ZUO啊)
从当前节点(i-1)更新下一节点(i)会好做一些,但是有个顺序问题,先从当前(i-1)跳一次到下一个(i),再更新从当前的位置跳了多次(及i这个位置),最后更新从上面往下落得情况。。。
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;struct use{ int p,l,h;}guan[10001];int x[10001]={0},y[10001]={0},f[10001][1001];int my_comp(const use &a,const use &b){ if (a.p<b.p) return 1; else return 0;}int main(){ freopen("bird.in","r",stdin); freopen("bird.out","w",stdout); int i,j,n,m,k,st,en,fl,ci,p,lj,maxn,maxx,ans,hh; bool ff=false; memset(f,127,sizeof(f)); maxx=f[0][0]; maxn=2100000000; scanf("%d%d%d",&n,&m,&k); for (i=0;i<n;++i) scanf("%d%d",&x[i],&y[i]); for (i=1;i<=k;++i) scanf("%d%d%d",&guan[i].p,&guan[i].l,&guan[i].h); sort(guan+1,guan+k+1,my_comp); for (i=1;i<=m;++i) f[0][i]=0; fl=1; for (i=1;i<=n;++i) { ans=maxx; st=1;en=m; if (guan[fl].p==i) { st=guan[fl].l+1; if (guan[fl].h==m) en=m; else en=guan[fl].h-1; ++fl; } if (st==0) st=1; for (j=1;j<=m;++j) { if (f[i-1][j]>maxn) continue; hh=min(j+x[i-1],m); f[i][hh]=min(f[i][hh],f[i-1][j]+1); if (ans>f[i][hh]) ans=f[i][hh]; } for (j=1;j<=m;++j) { if (f[i][j]>maxn) continue; hh=min(j+x[i-1],m); f[i][hh]=min(f[i][hh],f[i][j]+1); if (f[i][hh]<ans) ans=f[i][hh]; } for (j=1;j<=m;++j) { if (f[i-1][j]>maxn) continue; hh=j-y[i-1]; if (hh<=0) continue; f[i][hh]=min(f[i][hh],f[i-1][j]); if (f[i][hh]<ans) ans=f[i][hh]; } if (ans>maxn) { ff=true; ci=fl-2; break; } for (j=0;j<st;++j) f[i][j]=maxx; for (j=en+1;j<=m;++j) f[i][j]=maxx; } if (ff) printf("0\n%d\n",ci); else printf("1\n%d\n",ans); return(0); fclose(stdin); fclose(stdout);}
D2T1 水水的。。。这该叫什么?
#include<iostream>#include<cstdio>#include<cstdlib>using namespace std;int map[150][150]={0};int main(){ freopen("wireless.in","r",stdin); freopen("wireless.out","w",stdout); int n,x,y,u,ansi=0,dd,x1,x2,y1,y2,i,j,p,q,maxx=0,maxy=0; long long ans=0,sum; scanf("%d",&dd); scanf("%d",&n); for (i=1;i<=n;++i) { scanf("%d%d%d",&x,&y,&u); map[x][y]=u; if (x>maxx) maxx=x; if (y>maxy) maxy=y; } maxx=maxx+dd; if (maxx>128) maxx=128; maxy=maxy+dd; if (maxy>128) maxy=128; ansi=1; for (i=0;i<=maxx;++i) for (j=0;j<=maxy;++j) { x1=i-dd; if (x1<0) x1=0; x2=i+dd; if (x2>128) x2=128; y1=j-dd; if (y1<0) y1=0; y2=j+dd; if (y2>128) y2=128; sum=0; for (p=x1;p<=x2;++p) for (q=y1;q<=y2;++q) sum+=map[p][q]; if (sum==ans) ++ansi; else { if (sum>ans) { ans=sum; ansi=1; } } } printf("%d %lld\n",ansi,ans); fclose(stdin); fclose(stdout); return(0);}
D2T2 水水的深搜、最短路
考试的时候又写残了。。。(我与第二题。。。有个美丽的约定)(当时竟然深搜最短路,我的脑子里面。。。装了些啥。。。)
存的就是整个图的反图,然后先深搜一遍,将所有终点能到的点标记,然后对于不能到的点进行这个反图中这个点出发的边的终点进行去点,这是递归的,因为可能去掉很多个点(不一定是一直去点,要判断这个点本身能不能到)。(有点。。。我的语文啊)
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>using namespace std;int next[400001]={0},point[10001]={0},en[400001]={0},dis[10001]={0},que[1000001]={0};bool visit[10001]={false},cut[10001]={false};void dfs(int i,int dep){ int j; visit[i]=true; if (dis[i]>dep) dis[i]=dep; j=point[i]; while(j!=0) { if (!visit[en[j]]) dfs(en[j],dep+1); j=next[j]; }}void work(int i){ int j; cut[i]=true; if (visit[i]) return; j=point[i]; while (j!=0) { if (!cut[en[j]]) work(en[j]); j=next[j]; }}int main(){ freopen("road.in","r",stdin); freopen("road.out","w",stdout); int n,m,i,j,k,s,t,u,v,maxn=0,head,tail,x,y; bool ff=false; maxn=1000000; scanf("%d%d",&n,&m); for (i=1;i<=m;++i) { scanf("%d%d",&u,&v); next[i]=point[v]; point[v]=i; en[i]=u; } scanf("%d%d",&s,&t); for (i=1;i<=n;++i) dis[i]=maxn; dfs(t,0); if (!visit[s]) ff=true; else { for (i=1;i<=n;++i) if (!visit[i]) work(i); for (i=1;i<=n;++i) dis[i]=maxn; memset(visit,false,sizeof(visit)); head=0;tail=1;que[1]=t;visit[t]=true;dis[t]=0; do{ head=head%1000000+1; x=que[head]; visit[x]=false; y=point[x]; while (y!=0) { if (!cut[en[y]]) { if (dis[x]+1<dis[en[y]]) { dis[en[y]]=dis[x]+1; if (!visit[en[y]]) { tail=tail%1000000+1; que[tail]=en[y]; visit[en[y]]=true; } } } y=next[y]; } }while(head!=tail); if (cut[s]||dis[s]==maxn) ff=true; } if (ff) printf("-1\n"); else printf("%d\n",dis[s]); fclose(stdin); fclose(stdout); return(0);}
D2T3 高大上的解方程
这个数据范围。。。醉了。。。考试的时候直接放弃掉了,写了30的代码。(有过写50分的冲动,但是想到超级复杂的各种高精度,于是,及时收手)。
这个题目的神奇正解,下面来分析一下(金策)。
数学上的引例:f(x)=0(%p)则f(x+p)=0(%p);
有了这个积累,就相对简单了。先取一个小质数p,穷举出1~p-1中的f(x),对于为0的点入队,然后再用一个较大的质数P进行判断,若f(x)仍为0,则是一个解。对于每个队中的元素,又可以将其加上p之后的解入队,进行扩展,知道循环队列首尾相撞。注意这里的%p要对读进来的a、求f(x)的中间变量、mi都进行操作。对于a是负数的情况,又可以通过p(||P)-a得出余数。这里的时间复杂度计算比较复杂,大概是O(n(nm)^(0.5))(对于详细的求解过程,看到TA的解释,真是!!!orz,o(np+nnm\p)可以看做对勾函数,当p取(mn)^(0.5)时,食府最小)。
有一点小问题:在求解过程中的mi和f(x)都应用longlong以防止超大。
#include<iostream>#include<cstdio>#include<cstring>using namespace std;char s[100000];int a[101][3]={0},p0[3]={0,10007,100000009},ans[1000001]={0},n,m,que[1000001]={0},head=0,tail=0;bool work(int i,int j){ int k,p,q; long long sum=0,mi=1; i=i%p0[j]; sum=a[0][j]; for (k=1;k<=n;++k) { mi=(mi*i)%p0[j]; sum=(sum+a[k][j]*mi)%p0[j]; } if (sum==0) return true; return false;}int main(){ freopen("equation.in","r",stdin); freopen("equation.out","w",stdout); int i,j,l; bool ff=false; scanf("%d%d",&n,&m); for (i=0;i<=n;++i) { scanf("%s",&s); ff=false; l=strlen(s); for (j=0;j<l;++j) { if (s[j]==‘-‘) { ff=true; continue; } a[i][1]=(a[i][1]*10+s[j]-‘0‘)%p0[1]; a[i][2]=(a[i][2]*10+s[j]-‘0‘)%p0[2]; } if (ff) { a[i][1]=p0[1]-a[i][1]; a[i][2]=p0[2]-a[i][2]; } } for (i=1;i<p0[1];++i) if (work(i,1)) { ++tail;que[tail]=i; } do{ head=head%1000000+1; if (work(que[head],2)) { ++ans[0]; ans[ans[0]]=que[head]; } if (que[head]+p0[1]<=m) { tail=tail%1000000+1; que[tail]=que[head]+p0[1]; } }while(head!=tail); printf("%d\n",ans[0]); for (i=1;i<=ans[0];++i) printf("%d\n",ans[i]); fclose(stdin); fclose(stdout);}
noip2014就这样“轰轰荡荡”的过去了,今年的内心几经沉浮,最后看到自己的成绩的时候,一点小庆幸,当然以后不会有这么好的运气了,要真正能在考场上考出水准,离不开平日里多下功夫。明年就要迎来第一次省选了,希望能不留遗憾。
下面来说一说考场上的。。。错误:1)无奈的建错了树,这种错误在平时遇到过,却没有借鉴;2)竟然用dfs求最短路,检查的时候也没查出来,竟然还有点得意的往上交,这种基础的问题不应该出现问题,以后要时刻警惕;3)要在考场上想出好的算法,然后实现,不能因为语言问题阻碍了自己的发挥,要在平时多练基础题,提高代码能力,加快手速、保证正确率,正确理解到代码的运行原理,避免不必要的错误。
要多下功夫了!!!加油!!!
noip2014 小结