【NOIP 2016 总结】
距离杯赛已经很久了,然而我现在才打总结。。
我好惨的说..两场才380。。。
DAY 1
第一题 toy
送分题,模拟的时候+一下再mod一下就好。
[当时打完这题就没再看一眼了,好方的说]
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define Maxn 100010 int a[Maxn]; char s[Maxn][20]; int main() { int n,m; scanf("%d%d",&n,&m); for(int i=0;i<n;i++) { scanf("%d",&a[i]); scanf("%s",s[i]); } int now=0; for(int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); y%=n; if(a[now]==1&&x==0) now=(now+y)%n; else if(a[now]==1) now=(now+n-y)%n; else if(x==0) now=(now+n-y)%n; else now=(now+y)%n; } printf("%s\n",s[now]); return 0; }
第二题 running
感觉这题是全场最难的【是吧??
但其实【啊好像也不是很难。。
怎么比赛的时候就脑子那么乱呢
我的方法是,把路径分成两段,然后lca的特殊算。
假如lca在st上面,假设这条路径对z有贡献(z在lca和st之间),那么dep[z]-dep[st]=w[z] => dep[z]-w[z]=dep[st]
那么对于z来说,就是求经过自己的路径中dep等于dep[z]-w[z]的有多少个。
对于路径,我们在st上打一个加入标记,在lca上打一个删除标记,那么对于z,就是求其子树和(每个点用dfs序做,问题变成求区间和,单点修改,区间询问,可以用树状数组维护)。
对于加入和删除,我们按照st的dep排序,对于每个点,我们按照dep[z]-dep[st]排序,值相等是一起做,这时O(n+m)的。
对于右半边的路径类似,有2*dep[lca]-dep[st]=dep[z]+w[z]。
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define Maxn 300010 #define Maxm 300010 struct node { int x,y,next,c; }t[Maxn*2];int len=0; int n,m; struct hp { int x,y,c; }q[Maxm],qq[Maxm],nq[Maxn]; int l1,l2; int first[Maxn]; void ins(int x,int y) { t[++len].x=x;t[len].y=y; t[len].next=first[x];first[x]=len; } bool cmp(hp x,hp y) {return x.c<y.c;} int son[Maxn],dep[Maxn],sm[Maxn]; int fa[Maxn]; void dfs(int x,int f) { sm[x]=1;dep[x]=dep[f]+1; fa[x]=f; son[x]=0; for(int i=first[x];i;i=t[i].next) if(t[i].y!=f) { int y=t[i].y; dfs(y,x); sm[x]+=sm[y]; if(sm[y]>sm[son[x]]) son[x]=y; } } int tp[Maxn],dfn[Maxn],rt[Maxn],cnt=0; void dfs2(int x,int tpp) { tp[x]=tpp;rt[x]=dfn[x]=++cnt; if(son[x]) dfs2(son[x],tpp),rt[x]=rt[son[x]]; for(int i=first[x];i;i=t[i].next) if(t[i].y!=fa[x]&&t[i].y!=son[x]) { int y=t[i].y; dfs2(y,y); rt[x]=rt[y]; } } int lca(int x,int y) { int tt; while(tp[x]!=tp[y]) { if(dep[tp[x]]<dep[tp[y]]) tt=x,x=y,y=tt; x=fa[tp[x]]; } if(dep[x]<dep[y]) return x; return y; } int w[Maxn],ans[Maxn],c[Maxn]; void add(int x,int y) { for(int i=x;i<=n;i+=i&(-i)) c[i]+=y; } int query(int l,int r) { int ans=0; for(int i=r;i>=1;i-=i&(-i)) ans+=c[i]; l--; for(int i=l;i>=1;i-=i&(-i)) ans-=c[i]; return ans; } void ffind() { int l,r; l=2,r=1; memset(c,0,sizeof(c)); for(int i=1;i<=n;i++) { int x=nq[i].x; if(i==1||nq[i].c!=nq[i-1].c) { for(int j=l;j<r;j++) { add(q[j].x,-1),add(q[j].y,1); } l=r; while(q[l].c<nq[i].c&&l<=l1) l++; r=l; } while(q[r].c==nq[i].c&&r<=l1) { add(q[r].x,1);add(q[r].y,-1); //printf("add %d\n",r); r++; } ans[x]+=query(dfn[x],rt[x]); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); ins(x,y);ins(y,x); } for(int i=1;i<=n;i++) scanf("%d",&w[i]); dfs(1,0); dfs2(1,1); l1=l2=0; memset(ans,0,sizeof(ans)); for(int i=1;i<=m;i++) { int x,y,z; scanf("%d%d",&x,&y); z=lca(x,y); q[++l1].x=dfn[x];q[l1].y=dfn[z];q[l1].c=dep[x]; qq[++l2].x=dfn[y];qq[l2].y=dfn[z];qq[l2].c=2*dep[z]-dep[x]; if(w[z]==dep[x]-dep[z]) ans[z]++; } sort(q+1,q+1+l1,cmp); for(int i=1;i<=n;i++) nq[i].x=i,nq[i].c=dep[i]+w[i]; sort(nq+1,nq+1+n,cmp); ffind(); for(int i=1;i<=l2;i++) q[i]=qq[i]; l1=l2; sort(q+1,q+1+l1,cmp); for(int i=1;i<=n;i++) nq[i].x=i,nq[i].c=dep[i]-w[i]; sort(nq+1,nq+1+n,cmp); ffind(); for(int i=1;i<=n;i++) printf("%d ",ans[i]); printf("\n"); return 0; }
第三题 classroom
其实是一个很明显的期望DP。【当时脑抽again = = toxic
状态f[i][j][0]表示前i段,用j次机会,最后一次不使用机会的期望值。
状态f[i][j][1]表示前i段,用j次机会,最后一次使用机会的期望值。
f[i][j][0]=min(f[i-1][j][0]+1.0*dis[c[i-1]][c[i]])
f[i][j][0]=min(f[i-1][j][1]+1.0*dis[d[i-1]][c[i]]*k[i-1]+(1-k[i-1])*dis[c[i-1]][c[i]]);
f[i][j][1]=min(f[i-1][j-1][0]+k[i]*(dis[c[i-1]][d[i]])+(1-k[i])*(dis[c[i-1]][c[i]]));
f[i][j][1]=min(f[i-1][j-1][1]+ k[i]*(dis[d[i-1]][d[i]]*k[i-1]+dis[c[i-1]][d[i]]*(1-k[i-1]))+ (1-k[i])*(dis[d[i-1]][c[i]]*k[i-1]+dis[c[i-1]][c[i]]*(1-k[i-1])));
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<queue> 6 #include<algorithm> 7 using namespace std; 8 #define Maxn 2010 9 #define INF 0xfffffff 10 11 int c[Maxn],d[Maxn]; 12 double f[Maxn][Maxn][2],k[Maxn]; 13 14 int n,m,v,e; 15 16 int mmin(int x,int y) {return x<y?x:y;} 17 double mymin(double x,double y) {return x<y?x:y;} 18 19 int dis[Maxn][Maxn]; 20 21 void floyd() 22 { 23 for(int l=1;l<=v;l++) 24 for(int i=1;i<=v;i++) 25 for(int j=1;j<=v;j++) 26 dis[i][j]=mmin(dis[i][j],dis[i][l]+dis[l][j]); 27 } 28 29 int main() 30 { 31 scanf("%d%d%d%d",&n,&m,&v,&e); 32 for(int i=1;i<=n;i++) scanf("%d",&c[i]); 33 for(int i=1;i<=n;i++) scanf("%d",&d[i]); 34 for(int i=1;i<=n;i++) scanf("%lf",&k[i]); 35 memset(dis,63,sizeof(dis)); 36 for(int i=1;i<=e;i++) 37 { 38 int x,y,c; 39 scanf("%d%d%d",&x,&y,&c); 40 dis[x][y]=mmin(dis[x][y],c); 41 dis[y][x]=mmin(dis[y][x],c); 42 } 43 for(int i=1;i<=v;i++) dis[i][i]=0; 44 floyd(); 45 46 for(int i=1;i<=n;i++) 47 for(int j=0;j<=n;j++) 48 f[i][j][0]=f[i][j][1]=INF; 49 double ans=INF; 50 f[1][0][0]=f[1][1][1]=0;f[1][1][0]=0; 51 for(int i=2;i<=n;i++) 52 for(int j=0;j<=i&&j<=m;j++) 53 { 54 //printf("%.2lf\n",ans); 55 //bu yong 56 f[i][j][0]=mymin(f[i][j][0],f[i-1][j][0]+1.0*dis[c[i-1]][c[i]]); 57 f[i][j][0]=mymin(f[i][j][0],f[i-1][j][1]+1.0*dis[d[i-1]][c[i]]*k[i-1]+(1-k[i-1])*dis[c[i-1]][c[i]]); 58 59 //yong 60 if(j>0) 61 { 62 f[i][j][1]=mymin(f[i][j][1],f[i-1][j-1][0]+k[i]*(dis[c[i-1]][d[i]])+(1-k[i])*(dis[c[i-1]][c[i]])); 63 f[i][j][1]=mymin(f[i][j][1],f[i-1][j-1][1]+ 64 k[i]*(dis[d[i-1]][d[i]]*k[i-1]+dis[c[i-1]][d[i]]*(1-k[i-1]))+ 65 (1-k[i])*(dis[d[i-1]][c[i]]*k[i-1]+dis[c[i-1]][c[i]]*(1-k[i-1]))); 66 } 67 } 68 for(int i=0;i<=m&&i<=n;i++) ans=mymin(ans,f[n][i][0]), 69 ans=mymin(ans,f[n][i][1]); 70 printf("%.2lf\n",ans); 71 return 0; 72 }
第一天 100+25+55 哭泣
第二题交了纯暴力,第三题错误DP。。
DAY 2
第一题 problem
用杨辉三角搞一搞就好了。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<algorithm> 5 #include<iostream> 6 using namespace std; 7 #define Maxn 2010 8 9 int p[Maxn],f[Maxn][Maxn]; 10 int k; 11 12 13 int c[Maxn][Maxn],ans[Maxn][Maxn]; 14 15 void init() 16 { 17 for(int i=0;i<=2000;i++) c[i][0]=1; 18 for(int i=1;i<=2000;i++) 19 for(int j=1;j<=i;j++) 20 c[i][j]=(c[i-1][j-1]+c[i-1][j])%k; 21 22 memset(f,0,sizeof(f)); 23 for(int i=1;i<=2000;i++) 24 { 25 f[i][0]=0; 26 for(int j=1;j<=i;j++) 27 { 28 f[i][j]=f[i][j-1]; 29 if(c[i][j]==0) f[i][j]++; 30 } 31 for(int j=i+1;j<=2000;j++) f[i][j]=f[i][j-1]; 32 } 33 for(int i=1;i<=2000;i++) ans[1][i]=f[1][i]; 34 for(int i=2;i<=2000;i++) 35 for(int j=1;j<=2000;j++) ans[i][j]=ans[i-1][j]+f[i][j]; 36 } 37 38 int main() 39 { 40 int T; 41 scanf("%d%d",&T,&k); 42 init(); 43 while(T--) 44 { 45 int n,m; 46 scanf("%d%d",&n,&m); 47 printf("%d\n",ans[n][m]); 48 } 49 return 0; 50 }
第二题 earthworm
在暴力的基础上看出来有单调性就可以AC了。
切出来的左边递减,右边也递减,每次取三部分的最大值出来切,注意不要想成全部+q,用一个累加器,然后切出来的那部分-q是一样的。
//代码很丑,因为在洛谷上调TLE,,洛谷好慢的说。。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<algorithm> 5 #include<iostream> 6 using namespace std; 7 #define Maxn 100010 8 #define Maxm 7000010 9 #define INF 0xfffffff 10 #define LL long long 11 12 int a[Maxn]; 13 14 LL add=0; 15 LL v1[Maxm],v2[Maxm]; 16 17 int n,m,q,u,v,t; 18 int l1,l2; 19 20 bool cmp(int x,int y) {return x>y;} 21 22 23 int main() 24 { 25 scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t); 26 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 27 sort(a+1,a+1+n,cmp); 28 l1=1,l2=1; 29 v1[0]=v2[0]=0; 30 a[0]=1; 31 for(LL i=1;i<=m;i++) 32 { 33 34 35 LL now=-INF; 36 now*=10000000; 37 if(a[0]<=n) now=now>a[a[0]]?now:a[a[0]]; 38 if(l1<=v1[0]) now=now>v1[l1]?now:v1[l1]; 39 if(l2<=v2[0]) now=now>v2[l2]?now:v2[l2]; 40 if(a[0]<=n&&now==a[a[0]]) a[0]++; 41 else if(l1<=v1[0]&&now==v1[l1]) l1++; 42 else if(l2<=v2[0]&&now==v2[l2]) l2++; 43 now+=add; 44 45 46 47 48 49 50 if(i%t==0) printf("%lld ",now); 51 add+=q; 52 LL x=now*u/v,y=now-x; 53 v1[++v1[0]]=x-add;v2[++v2[0]]=y-add; 54 } 55 printf("\n"); 56 for(int i=1;i<=n+m;i++) 57 { 58 59 60 61 LL now=-INF; 62 now*=10000000; 63 if(a[0]<=n) now=now>a[a[0]]?now:a[a[0]]; 64 if(l1<=v1[0]) now=now>v1[l1]?now:v1[l1]; 65 if(l2<=v2[0]) now=now>v2[l2]?now:v2[l2]; 66 if(a[0]<=n&&now==a[a[0]]) a[0]++; 67 else if(l1<=v1[0]&&now==v1[l1]) l1++; 68 else if(l2<=v2[0]&&now==v2[l2]) l2++; 69 now+=add; 70 71 if(i%t==0) printf("%lld ",now); 72 } 73 printf("\n"); 74 return 0; 75 }
第三题 angrybirds
其实就是状压dp啊
注意精度,不要*100然后强转int【我就死在强转int,不然用round也可以
还有一个小地方就是,你可能会枚举两个点,是18^2的,但是其实你可以只枚举一个点,规定这一次一定先把某头猪弄下来(因为你后面总有一次要把这头猪弄下来的,不如现在先做了,反正没什么区别),这样就是一个18,这样就可以过了。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<algorithm> 5 #include<iostream> 6 using namespace std; 7 #define Maxd 300010 8 #define INF 0xfffffff 9 10 double xx[20],yy[20]; 11 12 int f[Maxd]; 13 int n,m; 14 15 int gcd(int a,int b) 16 { 17 if(b==0) return a; 18 return gcd(b,a%b); 19 } 20 21 double fabs(double x) {return x<0?-x:x;} 22 23 int sum; 24 int get_s(int a,int b,int s) 25 { 26 int ss=s; 27 double A,B; 28 A=(xx[b]*yy[a]-xx[a]*yy[b])*1.0/(xx[b]*xx[a]*xx[a]-xx[a]*xx[b]*xx[b]); 29 B=(yy[a]-A*xx[a]*xx[a])*1.0/xx[a]; 30 if(A>=0) 31 { 32 return -1; 33 } 34 sum=0; 35 for(int i=1;i<=n;i++) if((1<<i-1)&s) 36 { 37 if(fabs(A*xx[i]*xx[i]+B*xx[i]-yy[i])<0.000001) ss^=(1<<i-1),sum++; 38 } 39 return ss; 40 } 41 42 int mymin(int x,int y) {return x<y?x:y;} 43 44 int ffind(int s) 45 { 46 if(f[s]!=-1) return f[s]; 47 f[s]=INF; 48 for(int i=1;i<=n;i++) if((1<<i-1)&s) 49 { 50 for(int j=i+1;j<=n;j++) if((1<<j-1)&s) 51 { 52 int ss=get_s(i,j,s); 53 if(ss!=-1) f[s]=mymin(f[s],ffind(ss)+1); 54 } 55 break; 56 } 57 for(int i=1;i<=n;i++) if((1<<i-1)&s) 58 { 59 f[s]=mymin(f[s],ffind(s-(1<<i-1))+1); 60 break; 61 } 62 return f[s]; 63 } 64 65 int main() 66 { 67 int T; 68 scanf("%d",&T); 69 while(T--) 70 { 71 scanf("%d%d",&n,&m); 72 for(int i=1;i<=n;i++) 73 { 74 double nx,ny; 75 scanf("%lf%lf",&nx,&ny); 76 xx[i]=nx;yy[i]=ny; 77 } 78 memset(f,-1,sizeof(f)); 79 f[0]=0; 80 int ans=ffind((1<<n)-1); 81 printf("%d\n",ans); 82 } 83 return 0; 84 }
第二天 100+55+70 哭泣
被虐的好惨,主要是还是太傻了,其实题目说难也不是很难的。。。。
上面是bac回的AC代码。。。。
2016-12-07 14:02:45