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题解的主要内容,如果未能解决你的问题,请参考以下文章

NOIP2017题解

NOIP2017 题解

NOIP2017Day2题解

qboi冲刺NOIP2017复赛试题4 全套题目+题解+程序

NOIP2017总结 & 题解

NOIP2017-普及组复赛第2题 题解