NOIP2017题解
Posted moyiii
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP2017题解相关的知识,希望对你有一定的参考价值。
改题的过程中发现自己的思路有的很接近正解,但总是出了某些思维方式的偏差,所以或许还是写一写题解的好,毕竟游记都记的是一些乱七八糟的旅行经历和心理感受……正好中午饭前只有强行翘掉的一节课,就欣然划水了。
Day1
T1《小凯的疑惑》
得分:100
一道数学题,但是最合适的解法是打表找规律。记a为两个数中较小的一个,我打表的方式是对于每一个要check的数枚举$a$和$b$的系数暴力判断可行性,从$a+1$开始check,如果出现了连续的a个可行解就说明后面的每一个数$x$都可以由$(x-a)+1*a$得到,就不会再出现不可行解了。打出表来发现是个以$a-1$为公差的等差数列而$a$和$a+1$的答案是$a*(a+1)-a-(a+1)$,对拍了一下发现比较靠谱。实际上最简单的公式是$a*b-a-b$。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 long long ai,bi,jy,ans; 7 int main() 8 { 9 //freopen("data.in","r",stdin); 10 //freopen("me.out","w",stdout); 11 freopen("math.in","r",stdin); 12 freopen("math.out","w",stdout); 13 scanf("%lld%lld",&ai,&bi); 14 if(ai==1||bi==1) 15 { 16 printf("0"); 17 fclose(stdin);fclose(stdout); 18 return 0; 19 } 20 if(ai>bi) 21 { 22 jy=bi; 23 bi=ai,ai=jy; 24 } 25 ans=ai*(ai+1)-(ai+ai+1); 26 ans+=(ai-1)*(bi-ai-1); 27 printf("%lld",ans); 28 fclose(stdin);fclose(stdout); 29 return 0; 30 }
T2《时间复杂度》
得分:100
一道中等的模拟题,比往年的T1难一点但是也没有难到大模拟的地步。主要应用stack,在每一个循环结束时都把状态还原为开始循环之前。我维护了这个循环对应的变量、复杂度是否为$n$、是否进入了循环。总复杂度就是读入复杂度。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<stack> 6 #include<algorithm> 7 using namespace std; 8 int ca,li,g,len1,nt,mx,tp,wz,zz; 9 bool h[30],op,w,jr,qn,hn; 10 char s1[20]; 11 stack<int> ha; 12 stack<int> fh; 13 stack<int> pr; 14 int main() 15 { 16 freopen("complexity.in","r",stdin); 17 freopen("complexity.out","w",stdout); 18 scanf("%d",&ca); 19 for(int i=1;i<=ca;i++) 20 { 21 scanf("%d%s",&li,s1); 22 len1=strlen(s1); 23 jr=1; 24 g=mx=op=w=nt=mx=0; 25 memset(h,0,sizeof(h)); 26 while(!pr.empty()) pr.pop(); 27 while(!ha.empty()) ha.pop(); 28 while(!fh.empty()) fh.pop(); 29 for(int j=0;j<len1;j++) 30 { 31 if(s1[j]>=‘0‘&&s1[j]<=‘9‘) 32 g*=10,g+=s1[j]-‘0‘; 33 if(s1[j]==‘n‘) op=1; 34 } 35 for(int j=1;j<=li;j++) 36 { 37 scanf("%s",s1); 38 if(s1[0]==‘F‘) 39 { 40 scanf("%s",s1); 41 if(h[s1[0]-‘a‘]) w=1; 42 h[s1[0]-‘a‘]=1,fh.push(s1[0]-‘a‘); 43 pr.push(jr); 44 scanf("%s",s1); 45 if(s1[0]==‘n‘) qn=1; 46 else 47 { 48 qn=wz=0; 49 len1=strlen(s1); 50 for(int k=0;k<len1;k++) 51 if(s1[k]>=‘0‘&&s1[k]<=‘9‘) 52 wz*=10,wz+=s1[k]-‘0‘; 53 } 54 scanf("%s",s1); 55 if(s1[0]==‘n‘) hn=1; 56 else 57 { 58 hn=zz=0; 59 len1=strlen(s1); 60 for(int k=0;k<len1;k++) 61 if(s1[k]>=‘0‘&&s1[k]<=‘9‘) 62 zz*=10,zz+=s1[k]-‘0‘; 63 } 64 if((qn&&hn)||(!qn&&!hn)) ha.push(0); 65 if(!qn&&!hn&&wz>zz) jr=0; 66 if(qn&&!hn) jr=0,ha.push(0); 67 if(jr&&!qn&&hn) ha.push(1),nt++; 68 if(!jr&&!qn&&hn) ha.push(0); 69 if(nt>mx) mx=nt; 70 } 71 else 72 { 73 if(fh.empty()){ w=1;continue; } 74 tp=fh.top(),fh.pop(); 75 h[tp]=0; 76 tp=ha.top(),ha.pop(); 77 if(tp==1) nt--; 78 jr=pr.top(),pr.pop(); 79 } 80 } 81 if(!fh.empty()) w=1; 82 if(w==1) 83 { 84 printf("ERR\n"); 85 continue; 86 } 87 if((op==0&&mx==0)||(op==1&&mx==g)) 88 { 89 printf("Yes\n"); 90 continue; 91 } 92 printf("No\n"); 93 } 94 fclose(stdin);fclose(stdout); 95 //while(1); 96 return 0; 97 }
T3《逛公园》
得分:10
在考场上就没有足够的信心A掉这题,所以即使前两题稳而且时间充足也没有坚持去想正解,出现了问题没有积极想怎样去解决。以后要避免这种问题,考试策略应该主要根据题目的情况而不是经验,特别是决定弃掉某题应该仔细斟酌。当时主要卡在两个地方,一个是环的判断(实际上我几乎一开始就弃掉了这$30$分想都没想),另一个是合理的状态转移。考场上脑子一抽认为最短路的复杂度就没有$n^2$以下的,所以不知道怎么办。堆优化迪杰斯特拉的复杂度可以达到大概$nlogn$。状态数组是正确的,$f[i][j]$表示到$i$点距离为最短+$j$的方案数。用$dis[i]$表示到$i$的最短路长度,当时写的转移方程$f[v][dis[u]+w+j-dis[v]]=\sum{f[u][j]}$,而且用的转移顺序是奇奇怪怪的spfa序,相当于再写了一遍spfa。实际上正确的转移顺序是按照拓扑序记忆化搜索,而且正确的转移方程是$f[u][j]=\sum{f[v][j]-(dis[x]+w-dis[v])}$。对于环的判断,用0边跑tarjan缩点,check每一个0环里的点到$1$和到$n$的最短路之和是否小于等于$dis[n]+k$。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<queue> 5 #include<stack> 6 #define ll long long 7 #define mk make_pair 8 using namespace std; 9 inline int read() 10 { 11 int jg=0,jk=getchar()-‘0‘; 12 while(jk<0||jk>9) jk=getchar()-‘0‘; 13 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-‘0‘; 14 return jg; 15 } 16 const int sj=100010; 17 int n,m,k,p,ca,dis1[sj],disn[sj],h[sj],e,a1,a2,a3,o,d[sj]; 18 int dfn[sj],low[sj],bl[sj],size[sj]; 19 bool r[sj],op,ye; 20 ll f[sj][52],ans; 21 struct B 22 { 23 int ne,v,w; 24 }b[sj<<1],c[sj<<1]; 25 void add(int x,int y,int z) 26 { 27 b[e].v=y,b[e].w=z,b[e].ne=h[x],h[x]=e++; 28 c[o].v=x,c[o].w=z,c[o].ne=d[y],d[y]=o++; 29 } 30 typedef pair<int,int> di; 31 priority_queue<di , vector<di> , greater<di> > q; 32 stack<int> s; 33 void dij() 34 { 35 while(!q.empty()) q.pop(); 36 memset(r,0,sizeof(r)); 37 dis1[1]=0; 38 q.push(mk(0,1)); 39 while(!q.empty()) 40 { 41 a1=q.top().second,q.pop(); 42 if(r[a1]) continue; 43 r[a1]=1; 44 for(int i=h[a1];i!=-1;i=b[i].ne) 45 if(dis1[b[i].v]>dis1[a1]+b[i].w) 46 { 47 dis1[b[i].v]=dis1[a1]+b[i].w; 48 q.push(mk(dis1[b[i].v],b[i].v)); 49 } 50 } 51 while(!q.empty()) q.pop(); 52 memset(r,0,sizeof(r)); 53 disn[n]=0; 54 q.push(mk(0,n)); 55 while(!q.empty()) 56 { 57 a1=q.top().second,q.pop(); 58 if(r[a1]) continue; 59 r[a1]=1; 60 for(int i=d[a1];i!=-1;i=c[i].ne) 61 if(disn[c[i].v]>disn[a1]+c[i].w) 62 { 63 disn[c[i].v]=disn[a1]+c[i].w; 64 q.push(mk(disn[c[i].v],c[i].v)); 65 } 66 } 67 } 68 void init() 69 { 70 n=read(),m=read(),k=read(),p=read(); 71 memset(dis1,0x7f,sizeof(dis1)); 72 memset(disn,0x7f,sizeof(disn)); 73 memset(h,-1,sizeof(h));memset(d,-1,sizeof(d)); 74 memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low)); 75 memset(f,-1,sizeof(f));memset(size,0,sizeof(size)); 76 e=o=ans=op=ye=0; 77 for(int i=1;i<=m;i++) 78 { 79 a1=read(),a2=read(),a3=read(); 80 add(a1,a2,a3); 81 if(!a3) ye=1; 82 } 83 } 84 inline void bj(int &x,int y) 85 { 86 x=x<y?x:y; 87 } 88 void tarjan(int x) 89 { 90 dfn[x]=low[x]=++a1; 91 r[x]=1,s.push(x); 92 for(int i=h[x];i!=-1;i=b[i].ne) 93 { 94 if(b[i].w) continue; 95 if(!dfn[b[i].v]) tarjan(b[i].v),bj(low[x],low[b[i].v]); 96 else if(r[b[i].v]) bj(low[x],dfn[b[i].v]); 97 } 98 if(low[x]==dfn[x]) 99 { 100 a3++; 101 do 102 { 103 a2=s.top(),s.pop(); 104 r[a2]=0; 105 bl[a2]=a3,size[a3]++; 106 }while(a2!=x); 107 } 108 } 109 void check() 110 { 111 memset(r,0,sizeof(r)); 112 a1=a2=a3=0; 113 while(!s.empty()) s.pop(); 114 for(int i=1;i<=n;i++) 115 if(!dfn[i]) 116 tarjan(i); 117 for(int i=1;i<=n;i++) 118 if(size[bl[i]]!=1&&dis1[i]+disn[i]<=dis1[n]+k) 119 op=1; 120 } 121 ll dp(int x,int aim) 122 { 123 if(x==n&&aim==0) f[x][aim]=1; 124 if(f[x][aim]!=-1) return f[x][aim]; 125 f[x][aim]=0; 126 for(int i=h[x];i!=-1;i=b[i].ne) 127 if(aim-(dis1[x]+b[i].w-dis1[b[i].v])>=0) 128 { 129 dp(b[i].v,aim-(dis1[x]+b[i].w-dis1[b[i].v])); 130 f[x][aim]=(f[x][aim]+f[b[i].v][aim-(dis1[x]+b[i].w-dis1[b[i].v])])%p; 131 } 132 return f[x][aim]; 133 } 134 int main() 135 { 136 ca=read(); 137 for(int l=1;l<=ca;l++) 138 { 139 init(); 140 dij(); 141 if(ye) check(); 142 if(op) printf("%d\n",-1); 143 else 144 { 145 for(int i=0;i<=k;i++) dp(1,i); 146 for(int i=0;i<=k;i++) ans=(ans+f[1][i])%p; 147 printf("%lld\n",ans); 148 } 149 } 150 return 0; 151 }
Day2
T1《奶酪》
得分:100
如果说Day1T2是在考stack,那么这题就是在考queue了?应该存在无限多种$n^2$解法,我是用bfs实现的。队列一开始放所有从底面能直接到达的面,不断从队首扩展和它相连的球,直到队列空或到达了一个 能到达上顶面的球。需要稍微注意一下的就是用半径平方代替距离开根,而且距离的平方可能会炸long long,半径却不会炸,如果出负数可以直接判不可能(在极限数据下,或许会出现这种情况)。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<queue> 5 #include<algorithm> 6 #define ll long long 7 using namespace std; 8 const int sj=1010; 9 struct ch 10 { 11 ll xm,ym,zm; 12 }c[sj]; 13 int ca,n,tp; 14 ll h,r,dis,nt,d1,d2,d3; 15 bool vi[sj],op; 16 queue<int> q; 17 int main() 18 { 19 freopen("cheese.in","r",stdin); 20 freopen("cheese.out","w",stdout); 21 scanf("%d",&ca); 22 for(int l=1;l<=ca;l++) 23 { 24 scanf("%d%lld%lld",&n,&h,&r); 25 memset(vi,0,sizeof(vi)); 26 while(!q.empty()) q.pop(); 27 op=0; 28 for(int i=1;i<=n;i++) 29 { 30 scanf("%lld%lld%lld",&c[i].xm,&c[i].ym,&c[i].zm); 31 if(c[i].zm<=r) vi[i]=1,q.push(i); 32 } 33 dis=4*r*r; 34 while(!q.empty()) 35 { 36 tp=q.front(),q.pop(); 37 if(c[tp].zm>=h-r){ op=1;break; } 38 for(int i=1;i<=n;i++) 39 if(!vi[i]) 40 { 41 d1=(c[i].xm-c[tp].xm)*(c[i].xm-c[tp].xm); 42 if(d1>dis) continue; 43 d2=(c[i].ym-c[tp].ym)*(c[i].ym-c[tp].ym); 44 if(d2>dis) continue; 45 if(d1+d2>dis) continue; 46 d3=(c[i].zm-c[tp].zm)*(c[i].zm-c[tp].zm); 47 if(d3>dis) continue; 48 nt=d1+d2+d3; 49 if(nt>=0&&nt<=dis) vi[i]=1,q.push(i); 50 } 51 } 52 if(op==1) printf("Yes\n"); 53 else printf("No\n"); 54 } 55 fclose(stdin);fclose(stdout); 56 //while(1); 57 return 0; 58 }
以上是关于NOIP2017题解的主要内容,如果未能解决你的问题,请参考以下文章