NOIP2016:Day2解题报告

T1 组合数问题:

= =这道题在考场上的时候还不知道啥是组合数。。。。一看题就觉得哎呀好难啊。。。然而出来之后才听Xorex大神说这道题是杨辉三角???好吧当时实在是太菜了。。。好在写了个分解质因数的算法,原本期望80分的程序,结果我这个小蒟蒻不知道0!=1。。。。。惨啊

当初的SB代码好像已经没有了

分析:

  算法一:大力用公式和高精度求组合数,然后每次输入的时候判断一下矩阵内modk的值有几个等于0,输出答案。理想复杂度O(t*m*n),理想情况下的得分是10分

  算法二:在算法一的基础上运用求一下矩阵前缀和,理想复杂度是O(m*n*高精度),得分大概40?

  算法三:用分解因数的方法,判断一下ci,j中是否含有1个或以上的k因子,然后求一下矩阵前缀和,复杂度为O(m*n),理想得分是100分

  算法四:

    数据范围是2000,所以直接用n^2算法递推求出来所有的组合数就行,在求的时候需要mod一下k的值,否则会爆掉精度。

    那么如果ci,j是k的倍数的话,ci,j处的数一定为0,然后我们求一下矩阵的前缀和就行了。复杂度O(m*n),理想得分100分。

 1 //NOIP2017 D2T1
 2 //by:hinanawi
 3 #include<iostream>
 4 #include<cstdio>
 5 #include<cstring>
 6 #include<algorithm>
 7 #include<cctype>
 8 using namespace std;
 9 int n,m,a[2010][2010],sum[2010][2010],k,t;
10 int read(){
11     int x=0,y=1;char ch=getchar();
12     while (!isdigit(ch)){if (ch=='-')y=-1;ch=getchar();}
13     while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
14     return x*y;
15 }
16 int main(){
17     t=read();k=read();
18     for (int i=0;i<=2000;i++)
19         for (int j=0;j<=i;j++){
20             if (j==0){
21                 a[i][j]=1%k;
22             }
23             else{
24                 a[i][j]=(a[i-1][j-1]+a[i-1][j])%k;
25             }
26         }
27     if (a[0][0]==0)    sum[0][0]=1;
28     for (int i=1;i<=2000;i++)
29         for (int j=0;j<=i;j++){
30             if (j==0)    sum[i][j]=sum[i-1][j];
31             else if (i==j)    sum[i][j]=sum[i][j-1];
32             else sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
33             if (a[i][j]==0)    ++sum[i][j];
34         }
35     while (t--){
36         n=read();m=read();
37         printf("%d
",sum[n][min(n,m)]);
38     }
39     return 0;
40 }
算法四

T2 蚯蚓:

考场上的时候我连什么是队列都不知道。。。下去之后lzy大神告诉我优先队列可以水60分。。。然后我就用下面的算法一水了35分= =

分析:

  算法一:最裸的模拟,将所有的蚯蚓长度每次切过之后都加上q,然后再排序一遍。复杂度是O(mnlogn),理想得分35

  算法二:将所有蚯蚓加入一个优先队列中,然后每次全部出队之后加上q,再进队,复杂度同算法一,理想得分35

  算法三:仔细想想其实是不需要将每个蚯蚓出队之后再进队的,开一个结构体,然后记录一下蚯蚓出现的时间及出现时的长度,然后重载一下运算符,就能达到同样的效果,不过重载运算符的操作需要用手写堆来实现,比较麻烦,复杂度为O(mlogn),理想情况下可以拿到80分

  算法四:这个算法需要一个严格的证明:对于最初的蚯蚓来说,当一个蚯蚓比其它的蚯蚓长的话,它永远都比其他蚯蚓长,以及对于切除的蚯蚓ci,cj,i<j来说,其切掉的一条在k(k>j)时刻ci被切成的其中一条蚯蚓ai长度为ci*p+(k-i-1)*q,cj被切成的,与蚯蚓ai系数相同的蚯蚓aj长度为(ci+(j-i)*q)*p+(k-j-1)*q,即ci*p+(j-i)*pq+(k-j-1)*q,很明显ai>aj,这样就证明了蚯蚓的单调性,那么优先队列就没有了意义。所以我们可以开3个队列,第一个队列记录原蚯蚓,第二个队列记录被切成的系数为p的蚯蚓,第三个队列记录系数为1-p的蚯蚓,那么这三个队列都是严格递减的,那么我们每次取蚯蚓的时候讲三个队列的队首进行比较即可。时间复杂度为o(m),理想得分是100。但是!!!这道题输入和输出卡常数。。。需要用快读和快出来进行优化。

 1 //NOIP2016 D2T2
 2 //by:hinanawi
 3 #include<iostream>
 4 #include<cstdio>
 5 #include<cstring>
 6 #include<cctype>
 7 #include<algorithm>
 8 #include<queue>
 9 using namespace std;
10 int n,m,p,u,v,t,head[4],tail[4],a[100010];
11 struct node{
12     int v,t;
13 }q[4][7500010];
14 namespace IO{
15     char buf[1<<15],*fs,*ft;
16     inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
17     inline int read(){
18         int x=0,f=1;  char ch=getc();
19         while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getc();}
20         while(isdigit(ch))  {x=x*10+ch-'0';  ch=getc();}
21         return x*f;
22     }
23     void put(int x){
24         if(x==0){
25             putchar('0');
26             return;
27         }
28         if(x<0){
29             putchar('-');
30             x=-x;
31         }
32         int num=0;char ch[16];
33         while(x) ch[++num]=x%10+'0',x/=10;
34         while(num) putchar(ch[num--]);
35     }
36 };
37 using namespace IO;
38 
39 void init(){
40     n=read();m=read();p=read();u=read();v=read();t=read();
41     for (int i=1;i<=n;i++)    a[i]=read();
42     sort(a+1,a+1+n);
43     head[1]=1;tail[1]=0;
44     head[2]=1;tail[2]=0;
45     head[3]=1;tail[3]=0;
46     for (int i=n;i>=1;i--){
47         q[1][++tail[1]].v=a[i];q[1][tail[1]].t=0;
48     }
49 }
50 
51 void work(){
52     for (int i=1;i<=m;i++){
53         int maxx=0,pos=0;
54         if (head[1]<=tail[1]&&q[1][head[1]].v+(i-q[1][head[1]].t-1)*p>maxx){maxx=q[1][head[1]].v+(i-q[1][head[1]].t-1)*p;pos=1;}
55         if (head[2]<=tail[2]&&q[2][head[2]].v+(i-q[2][head[2]].t-1)*p>maxx){maxx=q[2][head[2]].v+(i-q[2][head[2]].t-1)*p;pos=2;}
56         if (head[3]<=tail[3]&&q[3][head[3]].v+(i-q[3][head[3]].t-1)*p>maxx){maxx=q[3][head[3]].v+(i-q[3][head[3]].t-1)*p;pos=3;}
57         int a1=1LL*maxx*u/v;
58         int a2=maxx-a1;
59         head[pos]++;
60         if (i%t==0){put(maxx);putchar(' ');}
61         q[2][++tail[2]].v=a1;q[2][tail[2]].t=i;
62         q[3][++tail[3]].v=a2;q[3][tail[3]].t=i;
63     }
64     putchar('
');
65     for (int i=1;i<=m+n;i++){
66         int maxx=0,pos=0;
67         if (head[1]<=tail[1]&&q[1][head[1]].v+(m-q[1][head[1]].t)*p>maxx){maxx=q[1][head[1]].v+(m-q[1][head[1]].t)*p;pos=1;}
68         if (head[2]<=tail[2]&&q[2][head[2]].v+(m-q[2][head[2]].t)*p>maxx){maxx=q[2][head[2]].v+(m-q[2][head[2]].t)*p;pos=2;}
69         if (head[3]<=tail[3]&&q[3][head[3]].v+(m-q[3][head[3]].t)*p>maxx){maxx=q[3][head[3]].v+(m-q[3][head[3]].t)*p;pos=3;}
70         head[pos]++;
71         if (i%t==0){put(maxx);putchar(' ');}
72     }
73     putchar('
');
74 }
75 
76 int main(){
77     init();
78     work();
79     return 0;
80 }
算法四+读入输出优化

T3 愤怒的小鸟:

这道题考场上一脸懵逼。。。因为当初什么都不会嘛QAQ

分析:

  算法一:DFS+回溯,理想分数20-100?

  算法二:我们可以发现DFS的过程是可以记录状态的。所以我们用状态压缩的方式,将所有在以i,j为两个端点的抛物线上的点用二进制的方式记录下来,然后做一遍状压DP就行了。根据DP的方式,分数80-100不等。我用的是类似背包的方法。

 1 //NOIP2016 D2T3
 2 //by:hinanawi
 3 #include<iostream>
 4 #include<cstdio>
 5 #include<cstring>
 6 #include<cctype>
 7 #include<algorithm>
 8 #include<cmath>
 9 using namespace std;
10 #define eps 0.000001
11 struct node{
12     double x,y;
13 }bird[20];
14 int T,n,m,flag[20][20],c[10010],tot=0;
15 double x[20],y[20],a[1<<18],b[1<<18];
16 int p[20];
17 int f[1<<18];
18 int read(){
19     int x=0,y=1;char ch=getchar();
20     while (!isdigit(ch)){if (ch=='-')y=-1;ch=getchar();}
21     while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
22     return x*y;
23 }
24 
25 bool mycmp(node a,node b)
26 {return a.x<b.x||a.x==b.x&&a.y<b.y;}
27 
28 inline void sol(int i,int j){
29     a[p[i]+p[j]]=(y[j]*x[i]-y[i]*x[j])/((x[j]-x[i])*x[j]*x[i]);
30     b[p[i]+p[j]]=(y[j]*x[i]*x[i]-y[i]*x[j]*x[j])/((x[i]-x[j])*x[i]*x[j]);
31 }
32 
33 void init(){
34     memset(f,0x3f,sizeof(f));
35     memset(flag,0,sizeof(flag));
36     tot=0;
37     f[0]=0;
38     n=read();m=read();
39     for (int i=1;i<=n;i++)    scanf("%lf%lf",&bird[i].x,&bird[i].y);
40     sort(bird+1,bird+1+n,mycmp);//不知道为什么这里不排序的话会有一组数据wa掉
41     for (int i=1;i<=n;i++){
42         x[i]=bird[i].x;    y[i]=bird[i].y;
43     }
44 }
45 
46 
47 
48 int main(){
49     T=read();
50     p[1]=1;
51     for (int i=2;i<=19;i++)    p[i]=p[i-1]<<1;
52     while (T--){
53         init();
54         for (int i=1;i<=n;i++)
55             for (int j=i+1;j<=n;j++)    sol(i,j);
56         for (int i=1;i<=n;i++){
57             flag[i][i]=p[i];
58             for (int j=i+1;j<=n;j++){
59                 if (a[p[i]+p[j]]>=-eps)    continue;
60                 flag[i][j]=p[i]+p[j];
61                 f[p[i]+p[j]]=1;
62                 for (int k=j+1;k<=n;k++){
63                     if (fabs(a[p[i]+p[j]]-a[p[j]+p[k]])<=eps&&fabs(b[p[i]+p[j]]-b[p[j]+p[k]])<=eps)    flag[i][j]+=p[k];    
64                     f[flag[i][j]]=1;
65                 }
66             }
67         }
68         for (int i=1;i<=n;i++)    f[p[i]]=1;
69         for (int i=1;i<=n;i++)
70             for (int j=i;j<=n;j++)
71                 if (flag[i][j]!=0)
72                     c[++tot]=flag[i][j];
73         sort(c+1,c+1+tot);
74         for (int i=1;i<=p[n+1]-1;i++){
75             if (f[i]!=0x3f3f3f3f)    continue;
76             for (int j=1;j<=tot&&c[j]<=i;j++){
77                 if ((c[j]&i)!=c[j])    continue;
78                 f[i]=min(f[i],f[i-c[j]]+1);
79             }
80         }
81         printf("%d
",f[p[n+1]-1]);
82     }
83     return 0;
84 }
算法二AC代码

 总结:NOIP2016day2还是很常规的一套题,但整体难度要小于day1。偏重考察思维能力,细节方面要小于去年,T1T3在数据范围方面都有点一眼题的意味,期望得分应该在200分以上(100+60+40),然而当初菜的一匹的我只拿了75分。。。(40+35+0)

原文地址:https://www.cnblogs.com/hinanawitenshi/p/7097481.html