[NOIP补坑计划]NOIP2016 题解&做题心得

感觉16年好难啊QAQ,两天的T2T3是不是都放反了啊……

场上预计得分:100+80+100+100+65+100=545(省一分数线280)

ps:loj没有部分分,部分分见洛咕

题解:

D1T1 玩具谜题

题面

水题送温暖~当然主要关注点都在mengbier和mogician上2333

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 #define eps 1e-9
 9 using namespace std;
10 typedef long long ll;
11 int n,m,nw=0,x,op,a[100001];
12 char s[100001][11];
13 int main(){
14     scanf("%d%d",&n,&m);
15     for(int i=0;i<n;i++){
16         scanf("%d%s",&a[i],s[i]);
17     }
18     for(int i=1;i<=m;i++){
19         scanf("%d%d",&op,&x);
20         if(op^a[nw])nw=(nw+x)%n;
21         else nw=(nw+n-x)%n;
22     }
23     printf("%s",s[nw]);
24     return 0;
25 }

D1T2 天天爱跑步

题面

绝对是这六题里最难的一题……这题目难度与顺序无关啊……想了半小时只会80,yy了一个线段树合并调不出来,树上差分并不会,最后百度了一发才搞懂……

首先考虑S->T这一条路径,肯定是从S出发先向上走到lca,再向下走到T,那么分开讨论这两种情况;

1.S->lca,此时路径上能对答案产生贡献的点i要满足$dep[i]+w[i]=dep[s]$;

2.lca->T,此时路径上能对答案产生贡献的点i要满足$dep[i]-w[i]=dep[t]-L$(其中L表示S到T的总长度)

这里貌似可以树链剖分+线段树直接维护?但是我出于对出题人基本的信任并没有写……

正解是用桶维护深度,维护一个向上的桶u和一个向下的桶d,对整棵树dfs;

对于一条s到t的路径,扫到s时就在s的$u[dep[s]]$中加一个,lca退栈时就把影响减掉,扫到lca时就在$d[dep[t]]$中加一个,t退栈时就减掉(这就是树上差分的思想);

那么每次的答案$ans[i]$就等于子树中$u[dep[i]+w[i]]+d[dep[i]-w[i]]$的数量;

注意这里可能会有负数,所以要整体加上$MAXN$;

最后如果s到t是一条链那么答案会算重,要把答案减一;

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<cmath>
  6 #include<queue>
  7 #define inf 2147483647
  8 #define eps 1e-9
  9 #define MX 300001
 10 using namespace std;
 11 typedef long long ll;
 12 struct edge{
 13     int v,next;
 14 }a[600001];
 15 int n,m,u,v,l,tot=0,fa[300001][20],ans[300001],bb[600001],bs[300001],bg[300001],dep[300001],ss[300001],t[300001],z[300001],num[300001],head[300001];
 16 vector<int>s[300001],t1[300001],t2[300001];
 17 void add(int u,int v){
 18     a[++tot].v=v;
 19     a[tot].next=head[u];
 20     head[u]=tot;
 21 }
 22 void dfs(int u,int ff,int dpt){
 23     fa[u][0]=ff;
 24     dep[u]=dpt;
 25     for(int i=1;i<=19;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
 26     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
 27         int v=a[tmp].v;
 28         if(v!=ff){
 29             dfs(v,u,dpt+1);
 30         }
 31     }
 32 }
 33 int lca(int u,int v){
 34     if(dep[u]<dep[v])swap(u,v);
 35     int l=dep[u]-dep[v];
 36     for(int i=19;i>=0;i--){
 37         if((1<<i)&l){
 38             u=fa[u][i];
 39         }
 40     }
 41     if(u==v)return u;
 42     for(int i=19;i>=0;i--){
 43         if(fa[u][i]!=fa[v][i]){
 44             u=fa[u][i],v=fa[v][i];
 45         }
 46     }
 47     return fa[u][0];
 48 }
 49 void dfs1(int u,int ff){
 50     int nw=bs[dep[u]+num[u]];
 51     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
 52         int v=a[tmp].v;
 53         if(v!=ff){
 54             dfs1(v,u);
 55         }
 56     }
 57     bs[dep[u]]+=bg[u];
 58     ans[u]+=bs[dep[u]+num[u]]-nw;
 59     for(int i=0,ii=s[u].size();i<ii;i++){
 60         bs[s[u][i]]--;
 61     }
 62 }
 63 void dfs2(int u,int ff){
 64     int nw=bb[dep[u]-num[u]+MX];
 65     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
 66         int v=a[tmp].v;
 67         if(v!=ff){
 68             dfs2(v,u);
 69         }
 70     }
 71     for(int i=0,ii=t1[u].size();i<ii;i++){
 72         bb[t1[u][i]+MX]++;
 73     }
 74     ans[u]+=bb[dep[u]-num[u]+MX]-nw;
 75     for(int i=0,ii=t2[u].size();i<ii;i++){
 76         bb[t2[u][i]+MX]--;
 77     }
 78 }
 79 int main(){
 80     memset(head,-1,sizeof(head));
 81     scanf("%d%d",&n,&m);
 82     for(int i=1;i<n;i++){
 83         scanf("%d%d",&u,&v);
 84         add(u,v);
 85         add(v,u);
 86     }
 87     for(int i=1;i<=n;i++){
 88         scanf("%d",&num[i]);
 89     }
 90     dfs(1,0,1);
 91     for(int i=1;i<=m;i++){
 92         scanf("%d%d",&ss[i],&t[i]);
 93         z[i]=lca(ss[i],t[i]);
 94         l=dep[ss[i]]+dep[t[i]]-2*dep[z[i]];
 95         bg[ss[i]]++;
 96         s[z[i]].push_back(dep[ss[i]]);
 97         t1[t[i]].push_back(dep[t[i]]-l);
 98         t2[z[i]].push_back(dep[t[i]]-l);
 99     }
100     dfs1(1,0);
101     dfs2(1,0);
102     for(int i=1;i<=m;i++){
103         if(dep[z[i]]+num[z[i]]==dep[ss[i]])ans[z[i]]--;
104     }
105     for(int i=1;i<=n;i++){
106         printf("%d ",ans[i]);
107     }
108     return 0;
109 }

D1T3 换教室

题面

题面看起来很复杂,实际上并不难……

显然这是个期望dp,且路程期望具有可加性,那么设$f[i][j][0 / 1]$表示当前在第i个时间段,已经申请了j次,这次是否申请;

推出式子就好了……

设$dis[i][j]$表示i到j的最短路:

$f[i][j][0]=min\{f[i-1][j][0]+dis[c[i-1]][c[i]],f[i-1][j][1]+dis[c[i-1]][c[i]]\times(1-k[i-1])$

$+dis[d[i-1]][c[i]]\times k[i-1]\}$

$f[i][j][1]=min\{f[i-1][j-1][0]+dis[c[i-1]][c[i]]\times(1-k[i])+dis[c[i-1]][d[i]]\times k[i],$

$f[i-1][j-1][1]+dis[c[i-1]][c[i]]\times(1-k[i-1])\times(1-k[i])+dis[d[i-1]][c[i]]\times k[i-1]\times(1-k[i])$

$+dis[c[i-1]][d[i]]\times(1-k[i-1])\times k[i]+dis[d[i-1]][d[i]]\times k[i-1]\times k[i]\}$

由于v很小,所以可以直接用floyd求出所有点对之间的最短路,于是就做完了……

时间复杂度$O(nm+V^3)$

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 using namespace std;
 7 int n,m,v,e,u,vv,w,c[2001],d[2001],sp[2001][2001];
 8 double k[2000001],f[2001][2001][2],tt,ans;
 9 int main(){
10     memset(sp,0x3f,sizeof(sp));
11     scanf("%d%d%d%d",&n,&m,&v,&e);
12     for(int i=1;i<=n;i++){
13         scanf("%d",&c[i]);
14     }
15     for(int i=1;i<=n;i++){
16         scanf("%d",&d[i]);
17     }
18     for(int i=1;i<=n;i++){
19         scanf("%lf",&k[i]);
20     }
21     for(int i=0;i<=v;i++){
22         sp[i][i]=0;
23     }
24     for(int i=0;i<=n;i++){
25         for(int j=0;j<=m;j++){
26             f[i][j][0]=1e16;
27             f[i][j][1]=1e16;
28         }
29     } 
30     f[1][0][0]=f[1][1][1]=0;
31     for(int i=1;i<=e;i++){
32         scanf("%d%d%d",&u,&vv,&w);
33         if(u==vv){
34             continue;
35         }
36         sp[u][vv]=min(sp[u][vv],w);
37         sp[vv][u]=sp[u][vv];
38     }
39     for(int kk=1;kk<=v;kk++){
40         for(int i=1;i<=v;i++){
41             for(int j=1;j<=v;j++){
42                 sp[i][j]=min(sp[i][kk]+sp[kk][j],sp[i][j]);
43             }
44         }
45     }
46     for(int i=2;i<=n;i++){
47         f[i][0][0]=f[i-1][0][0]+sp[c[i-1]][c[i]];
48         for(int j=1;j<=min(m,i);j++){            
49             f[i][j][0]=min(f[i-1][j][0]+sp[c[i-1]][c[i]],f[i-1][j][1]+sp[c[i-1]][c[i]]*(1.0-k[i-1])+sp[d[i-1]][c[i]]*k[i-1]);
50             f[i][j][1]=f[i-1][j-1][0]+sp[c[i-1]][c[i]]*(1.0-k[i])+sp[c[i-1]][d[i]]*k[i];
51             tt=f[i-1][j-1][1]+sp[c[i-1]][c[i]]*(1.0-k[i-1])*(1.0-k[i])+sp[d[i-1]][c[i]]*k[i-1]*(1.0-k[i])+sp[c[i-1]][d[i]]*(1.0-k[i-1])*k[i]+sp[d[i-1]][d[i]]*k[i-1]*k[i];
52             f[i][j][1]=min(f[i][j][1],tt); 
53         }
54     }
55     ans=f[n][0][0];
56     for(int i=1;i<=m;i++){
57         ans=min(ans,min(f[n][i][1],f[n][i][0]));
58     }
59     printf("%.2lf",ans);
60     return 0;
61 }

D2T1 组合数问题

题面

并不是很水?要预处理前缀和;

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 #define eps 1e-9
 9 using namespace std;
10 typedef long long ll;
11 int n,m,t,k,s[2001][2001],C[2001][2001];
12 int main(){
13     scanf("%d%d",&t,&k);
14     C[0][0]=1;
15     for(int i=1;i<=2000;i++){
16         C[i][0]=1;
17         for(int j=1;j<=i;j++){
18             C[i][j]=(C[i-1][j]+C[i-1][j-1])%k;
19         }
20     }
21     for(int i=1;i<=2000;i++){
22         for(int j=1;j<=2000;j++){
23             s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(!C[i][j]&&i>=j);
24         }
25     }
26     while(t--){
27         scanf("%d%d",&n,&m);
28         printf("%d\n",s[n][m]);
29     }
30     return 0;
31 }

D2T2 蚯蚓

D2T3 愤怒的小鸟

总结:

猜你喜欢

转载自www.cnblogs.com/dcdcbigbig/p/9927081.html
今日推荐