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分。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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。但是!!!这道题输入和输出卡常数。。。需要用快读和快出来进行优化。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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不等。我用的是类似背包的方法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
总结:NOIP2016day2还是很常规的一套题,但整体难度要小于day1。偏重考察思维能力,细节方面要小于去年,T1T3在数据范围方面都有点一眼题的意味,期望得分应该在200分以上(100+60+40),然而当初菜的一匹的我只拿了75分。。。(40+35+0)