NOIP2017题解
Posted 蒟蒻ZJO :-)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP2017题解相关的知识,希望对你有一定的参考价值。
小凯的疑惑
a*b-a-b
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #include<stack> 8 #include<queue> 9 #include<set> 10 #include<vector> 11 #include<map> 12 #define LL long long 13 using namespace std; 14 int main(){ 15 LL a,b; 16 scanf("%lld%lld",&a,&b); 17 printf("%lld",a*b-(a+b)); 18 return 0; 19 }
时间复杂度
有点麻烦的模拟,注意细节.(出题人XXX)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #include<stack> 8 #include<queue> 9 #include<set> 10 #include<vector> 11 #include<map> 12 #define U unsigned 13 using namespace std; 14 struct data{ 15 char s[2],x[5],y[5]; 16 }fo[200]; 17 char o[10],fe[200][2]; 18 bool bj[30]; 19 int S[200]; 20 int main(){ 21 int T,n; 22 scanf("%d",&T); 23 while(T){T--; 24 scanf("%d%s",&n,o); 25 int sum=0;memset(bj,0,sizeof(bj));bool fl=1; 26 for(int i=1;i<=n;i++){ 27 scanf("%s",fe[i]); 28 if(fe[i][0]==‘F‘){ 29 scanf("%s%s%s",fo[i].s,fo[i].x,fo[i].y); 30 int ch=fo[i].s[0]-‘a‘; 31 if(bj[ch]) fl=0;bj[ch]=1; 32 S[++sum]=ch; 33 } 34 else bj[S[sum--]]=0; 35 } 36 if(sum!=0 || fl==0){printf("ERR\n");continue;} 37 int p=0,mx=0,top=0;bool fl1=1; 38 for(int i=1;i<=n;i++){ 39 if(fl1==0){ 40 int op=1; 41 while(1){ 42 if(i>n) break; 43 if(fe[i][0]==‘E‘) op--; 44 else if(fe[i][0]==‘F‘) op++; 45 if(op==0) break; 46 i++; 47 } 48 fl1=1; 49 continue; 50 } 51 if(fe[i][0]==‘F‘){ 52 if(fo[i].y[0]==‘n‘ && fo[i].x[0]==‘n‘){S[++top]=0;continue;} 53 else if(fo[i].y[0]==‘n‘){p++;S[++top]=1;continue;} 54 else if(fo[i].x[0]==‘n‘){fl1=0;continue;} 55 int xx=0,yy=0; 56 for(U int j=0;j<strlen(fo[i].x);j++) xx=xx*10+fo[i].x[j]-‘0‘; 57 for(U int j=0;j<strlen(fo[i].y);j++) yy=yy*10+fo[i].y[j]-‘0‘; 58 if(xx>yy) {fl1=0;continue;} 59 S[++top]=0; 60 } 61 else{mx=max(mx,p);if(S[top])p--;top--;} 62 } 63 mx=max(mx,p); 64 if(mx==0){ 65 if(o[2]==‘1‘) printf("Yes\n");else printf("No\n"); 66 } 67 else{ 68 int zz=0; 69 if(o[2]==‘1‘){printf("No\n");continue;} 70 for(int j=4;o[j]!=‘)‘;j++) zz=zz*10+o[j]-‘0‘; 71 if(zz==mx) printf("Yes\n"); 72 else printf("No\n"); 73 } 74 } 75 return 0; 76 }
逛公园
首先判-1
先正反两遍求出最短路.
然后Tarjan找0环,若从起点到0环的距离到终点的距离<=最短路+k,则-1.
设f[i][j]表示到了i点,比最短路多了j的路程的方案数,转移就比较显然了.
拓扑排序被卡常,记忆化搜索跑的贼快.
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #include<stack> 8 #include<queue> 9 #include<set> 10 #include<vector> 11 #include<map> 12 #define maxn 200010 13 #define mk make_pair 14 #define fi first 15 #define se second 16 #define RG register 17 using namespace std; 18 typedef pair<int,int> P; 19 struct data{ 20 int nex,to,w; 21 }e[maxn],g[maxn]; 22 int head[maxn],head1[maxn],edge1=0,edge=0,n,m,k,p,dis[maxn],dis1[maxn],f[maxn][55]; 23 int dfn[maxn],low[maxn],de=0,top=0; 24 int S[maxn]; 25 bool vis[maxn],scc[maxn],flag=0; 26 queue<int>q; 27 inline void add(RG int from,RG int to,RG int w){ 28 e[++edge]=(data){head[from],to,w}; 29 head[from]=edge; 30 g[++edge1]=(data){head1[to],from,w}; 31 head1[to]=edge1; 32 } 33 inline void getdis(){ 34 memset(dis,127/3,sizeof(dis)); 35 dis[1]=0,vis[1]=1,q.push(1); 36 while(!q.empty()){ 37 RG int u=q.front();q.pop(),vis[u]=0; 38 for(RG int i=head[u];i;i=e[i].nex){ 39 RG int v=e[i].to; 40 if(dis[v]>dis[u]+e[i].w){ 41 dis[v]=dis[u]+e[i].w; 42 if(!vis[v]) q.push(v),vis[v]=1; 43 } 44 } 45 } 46 } 47 inline void getdis1(){ 48 memset(dis1,127/3,sizeof(dis1)); 49 dis1[n]=0,vis[n]=1,q.push(n); 50 while(!q.empty()){ 51 RG int u=q.front();q.pop(),vis[u]=0; 52 for(RG int i=head1[u];i;i=g[i].nex){ 53 RG int v=g[i].to; 54 if(dis1[v]>dis1[u]+g[i].w){ 55 dis1[v]=dis1[u]+g[i].w; 56 if(!vis[v]) q.push(v),vis[v]=1; 57 } 58 } 59 } 60 } 61 inline void Tarjan(int x){ 62 dfn[x]=low[x]=++de; 63 S[++top]=x; 64 for(int i=head[x];i;i=e[i].nex) 65 if(e[i].w==0){ 66 int v=e[i].to; 67 if(!dfn[v])Tarjan(v),low[x]=min(low[x],low[v]); 68 else if(!scc[v]) low[x]=min(low[x],dfn[v]); 69 } 70 if(dfn[x]==low[x]){ 71 int sz=1; 72 while(S[top]!=x) scc[S[top--]]=1,sz++; 73 top--;scc[x]=1; 74 if(sz>1 && dis[x]+dis1[x]<=dis[n]+k)flag=1; 75 } 76 } 77 inline bool check(){ 78 flag=0;memset(scc,0,sizeof(scc)); 79 for(int i=1;i<=n;i++) 80 if(!dfn[i]) Tarjan(i); 81 return flag; 82 } 83 int dfs(int x,int pp){ 84 if(pp<0) return 0; 85 if(x==n && pp==0) return 1; 86 if(f[x][pp]!=-1) return f[x][pp]; 87 f[x][pp]=0; 88 for(int i=head[x];i;i=e[i].nex){ 89 int v=e[i].to; 90 f[x][pp]+=dfs(v,pp-(e[i].w+dis1[v]-dis1[x])); 91 if(f[x][pp]>=p) f[x][pp]-=p; 92 } 93 return f[x][pp]; 94 } 95 int main(){ 96 RG int T; 97 scanf("%d",&T); 98 while(T){T--; 99 scanf("%d%d%d%d",&n,&m,&k,&p); 100 memset(head,0,sizeof(head)),edge=edge1=de=top=0; 101 memset(head1,0,sizeof(head1));memset(f,-1,sizeof(f)); 102 memset(dfn,0,sizeof(dfn)),memset(low,0,sizeof(low)); 103 for(RG int i=1,x,y,z;i<=m;i++) 104 scanf("%d%d%d",&x,&y,&z),add(x,y,z); 105 getdis();getdis1(); 106 if(check()){printf("-1\n");continue;}; 107 RG int ans=0; 108 for(RG int i=0;i<=k;i++) ans=(ans+dfs(1,i))%p; 109 printf("%d\n",ans); 110 } 111 return 0; 112 }
奶酪
用公式算距离然后并查集即可.
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #include<queue> 8 #include<vector> 9 #include<stack> 10 #include<map> 11 #include<set> 12 #define maxn 1010 13 using namespace std; 14 double x[maxn],y[maxn],z[maxn],fa[maxn],r; 15 int find(int x){if(fa[x]!=x)fa[x]=find(fa[x]);return fa[x];} 16 inline void unionn(int i,int j){ 17 int u=find(i),v=find(j); 18 if(u!=v) fa[v]=u; 19 } 20 inline bool check(int i,int j){ 21 double dis=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])+(z[i]-z[j])*(z[i]-z[j])); 22 if(dis<=2*r) return 1; 23 else return 0; 24 } 25 int main(){ 26 int T,n;double h; 27 scanf("%d",&T); 28 while(T){T--; 29 scanf("%d%lf%lf",&n,&h,&r); 30 for(int i=1;i<=n;i++) 31 scanf("%lf%lf%lf",&x[i],&y[i],&z[i]),fa[i]=i; 32 int s=0,t=n+1;fa[s]=s,fa[t]=t; 33 for(int i=1;i<=n;i++){ 34 if(z[i]<=r) unionn(s,i); 35 if(z[i]>=h-r) unionn(t,i); 36 } 37 for(int i=1;i<=n;i++) 38 for(int j=i+1;j<=n;j++) 39 if(check(i,j)) unionn(i,j); 40 if(find(s)==find(t)) printf("Yes\n"); 41 else printf("No\n"); 42 } 43 return 0; 44 }
宝藏
感觉是今年正解最难的一道题,不过搜索+剪枝可以过.
子集DP.
设f[i][j]表示当前扩展到第i层,已经扩展到的节点集合为j的最小代价.
转移的话枚举补集,然后暴力算补集中的每个点到现在集合中的点的最小距离.
复杂度(3^n*n^2)
可以预处理每个点到每个集合的最短距离,复杂度可以优化一个n.
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #include<queue> 8 #include<vector> 9 #include<stack> 10 #include<map> 11 #include<set> 12 #include<ctime> 13 #define fi first 14 #define se second 15 #define mk make_pair 16 #define RG register 17 using namespace std; 18 int w[15][15],f[15][5010],n,m; 19 inline int solve(int s0,int s){ 20 int ans=0; 21 for(int i=0;i<n;i++) 22 if(s0&(1<<i)){ 23 int zd=500010; 24 for(int j=0;j<n;j++) 25 if(s&(1<<j))zd=min(zd,w[i][j]); 26 ans+=zd; 27 } 28 return ans; 29 } 30 int main(){ 31 int lim,ans,inf; 32 scanf("%d%d",&n,&m);lim=(1<<n)-1; 33 memset(f,127/3,sizeof(f));ans=inf=f[0][0]; 34 for(int i=0;i<n;i++) f[0][1<<i]=0; 35 for(int i=0;i<n;i++) 36 for(int j=0;j<n;j++)w[i][j]=500010; 37 for(RG int i=1,x,y,z;i<=m;i++) 38 scanf("%d%d%d",&x,&y,&z),x--,y--,w[x][y]=w[y][x]=min(w[x][y],z); 39 for(int i=0;i<n;i++) 40 for(int s=0;s<=lim;s++) 41 if(f[i][s]<inf){ 42 int U=lim^s; 43 for(int s0=U;s0;s0=(s0-1)&U){ 44 int wp=solve(s0,s); 45 f[i+1][s0|s]=min(f[i+1][s0|s],f[i][s]+wp*(i+1)); 46 } 47 } 48 for(int i=0;i<=n;i++) ans=min(ans,f[i][lim]); 49 printf("%d",ans); 50 return 0; 51 }
列队
若想到用"非NOIP"的做法来做还是不太难..
考场上思维局限在NOIP,导致这题挂烂.
想到动态开点线段树就比较容易了.
维护n+1棵线段树,n棵表示行,1棵表示最后一列.
对于每次出队,分两种情况讨论.
1.在最后一列出对.
在出队点的线段树的位置赋为1,表示这个地方已经没有人了.
然后把这个人插入到后面,往动态数组里面插.
所以线段树的大小为max(n,m)+q.
每次在线段树里面查询第k个0的位置即可.
2.不在最后一列出队.
把这一行的这个点删除,插入到最后一列最后一个中,然后把最后一列的这一行的删除,并且插入到这一行的最后一个中.
比平衡树容易实现得多.
代码十分短.
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #include<vector> 8 #include<queue> 9 #include<stack> 10 #include<map> 11 #include<set> 12 #define maxn 300010 13 #define LL long long 14 #define pb push_back 15 using namespace std; 16 int mx,n,m,cnt=0,rt[maxn*20],ls[maxn*20],rs[maxn*20],b[maxn*20]; 17 vector<LL>vec[maxn]; 18 void change(int &x,int l,int r,int sp){ 19 if(!x) x=++cnt;b[x]++; 20 if(l==r) return; 21 int mid=(l+r)>>1; 22 if(sp<=mid) change(ls[x],l,mid,sp); 23 else change(rs[x],mid+1,r,sp); 24 } 25 int query(int x,int l,int r,int k){ 26 if(l==r) return l; 27 int mid=(l+r)>>1,sum=mid-l+1-b[ls[x]]; 28 if(sum>=k) return query(ls[x],l,mid,k); 29 else return query(rs[x],mid+1,r,k-sum); 30 } 31 inline LL calc1(int x){ 32 int r=query(rt[n+1],1,mx,x); 33 change(rt[n+1],1,mx,r); 34 return r<=n?(LL)r*m:vec[n+1][r-n-1]; 35 } 36 inline LL calc2(int x,int y){ 37 int r=query(rt[x],1,mx,y); 38 change(rt[x],1,mx,r); 39 return r<m?(LL)(x-1)*m+r:vec[x][r-m]; 40 } 41 int main(){ 42 freopen("!.in","r",stdin); 43 freopen("!.out","w",stdout); 44 int q; 45 scanf("%d%d%d",&n,&m,&q); 46 mx=max(n,m)+q; 47 for(int i=1,x,y;i<=q;i++){ 48 LL ans; 49 scanf("%d%d",&x,&y); 50 if(y==m){ 51 ans=calc1(x); 52 printf("%lld\n",ans); 53 vec[n+1].pb(ans); 54 } 55 else{ 56 ans=calc2(x,y); 57 printf("%lld\n",ans); 58 LL tmp=calc1(x); 59 vec[n+1].pb(ans); 60 vec[x].pb(tmp); 61 } 62 } 63 return 0; 64 }
以上是关于NOIP2017题解的主要内容,如果未能解决你的问题,请参考以下文章