2016.10.30 NOIP模拟赛 day2 AM 整理
题目+数据:链接:http://pan.baidu.com/s/1gfBg4h1 密码:ho7o
总共得了:130分,
1:100分 2:30分(只会这30分的暴力) 3:0(毫无思路)
虽然不高,但是比较满意,因为把自己会的分数都拿到了。
T1:100分
1 /* 2 T1明显是个数论题。 3 正确的思路:把n!质因数分解,把所有质因数的指数都取到最大的偶数,它们的乘积便是最终的结果。 4 有一种很快的方法在Eular筛中可以n!的质因数分解。 5 if(!is_prim[i]) 6 { 7 prim[++prim[0]]=i; 8 ll x=n; 9 while(x) 10 { 11 cs[prim[0]]+=x/prim[prim[0]]; 12 x/=prim[prim[0]]; 13 } 14 } 15 具体的可以用“抽数法(反正我这么叫它)”来证明。 16 17 */ 18 #define N 5000010 19 #include<iostream> 20 using namespace std; 21 #include<cstdio> 22 #define Mod 100000007 23 typedef long long ll; 24 ll prim[N]={0}; 25 int cs[N]={0}; 26 bool is_prim[N]; 27 int n; 28 void eular() 29 { 30 for(ll i=2;i<=n;++i) 31 { 32 if(!is_prim[i]) 33 { 34 prim[++prim[0]]=i; 35 ll x=n; 36 while(x) 37 { 38 cs[prim[0]]+=x/prim[prim[0]]; 39 x/=prim[prim[0]]; 40 } 41 } 42 for(int j=1;j<=prim[0];++j) 43 { 44 if(i*prim[j]>n) break; 45 is_prim[i*prim[j]]=true; 46 if(i%prim[j]==0) break; 47 } 48 } 49 /* for(int i=1;i<=prim[0];++i) 50 printf("%d\n",prim[i]);*/ 51 } 52 void fj() 53 { 54 for(ll i=2;i<=n;++i) 55 { 56 ll x=i; 57 while(x!=1) 58 { 59 for(int j=1;j<=prim[0];++j) 60 { 61 if(x%prim[j]==0) 62 { 63 cs[j]++; 64 x/=prim[j]; 65 break; 66 } 67 } 68 } 69 } 70 } 71 ll quick_mod(ll a,ll b)//a^b 72 { 73 ll ret=1; 74 while(b) 75 { 76 if(b&1) 77 { 78 ret=(ret*a)%Mod; 79 } 80 b>>=1; 81 a=(a*a)%Mod; 82 } 83 return ret; 84 } 85 int main() 86 { 87 freopen("hao.in","r",stdin); 88 freopen("hao.out","w",stdout); 89 scanf("%d",&n); 90 eular(); 91 // fj();这是没用这个优化的部分,只能得50分。 92 ll ans=1; 93 for(int i=1;i<=prim[0];++i) 94 { 95 if(cs[i]%2==0) 96 { 97 ans=(ans*quick_mod(prim[i],cs[i]))%Mod; 98 } 99 else{ 100 ans=(ans*quick_mod(prim[i],cs[i]-1))%Mod; 101 } 102 } 103 cout<<ans%Mod<<endl; 104 fclose(stdin); 105 fclose(stdout); 106 return 0; 107 }
T2:
/*这道题目内部的转化非常巧妙,
首先[l,r]->[0,r]-[0,l-1]
现在只讨论r
Σxi/m -r<=0 --> ∑(xi-r)/m<=0 ==>∑(xi-m)<=0:
题目==>询问有多少区间和小于等于0
做一个前缀和S,现有[a,b] 要满足 s[b]-s[a]<=0 :
询问有多少对a,b使s[b]<=s[a] --> 求逆序对
注意这道题目的转换,十分的巧妙。
--------*****------
另外求逆序对有两种方法:归并排序或者树状数组,第二种请自行百度。
*/
1 #include<iostream> 2 using namespace std; 3 #include<cstdio> 4 #define N 500010 5 typedef long long ll; 6 ll a[N],sum[N],zc[N]; 7 int n,r,l; 8 ll read() 9 { 10 ll ret=0,ff=1; 11 char s=getchar(); 12 while(s<'0'||s>'9') 13 { 14 if(s=='-') ff=-1; 15 s=getchar(); 16 } 17 while(s>='0'&&s<='9') 18 { 19 ret=ret*10+s-'0'; 20 s=getchar(); 21 } 22 return ret*ff; 23 } 24 void gb1(int l1,int r1,ll &ans) 25 { 26 if(l1==r1) return; 27 int mid=(l1+r1)>>1; 28 gb1(l1,mid,ans); 29 gb1(mid+1,r1,ans); 30 int s=l1,i=l1,j=mid+1; 31 while(i<=mid&&j<=r1) 32 { 33 if(sum[i]>=sum[j]) 34 { 35 zc[s]=sum[j]; 36 ans+=mid-i+1; 37 s++;j++; 38 } 39 else{ 40 zc[s]=sum[i]; 41 i++;s++; 42 } 43 } 44 while(i<=mid) 45 { 46 zc[s]=sum[i]; 47 i++;s++; 48 } 49 while(j<=r1) 50 { 51 zc[s]=sum[j]; 52 s++;j++; 53 } 54 for(int i=l1;i<=r1;++i) 55 sum[i]=zc[i]; 56 } 57 void gb2(int l1,int r1,ll &ans) 58 { 59 if(l1==r1) return ; 60 int mid=(l1+r1)>>1; 61 gb2(l1,mid,ans); 62 gb2(mid+1,r1,ans); 63 int i=l1,s=l1,j=mid+1; 64 while(i<=mid&&j<=r1) 65 { 66 if(sum[i]>sum[j]) 67 { 68 ans-=(mid-i+1); 69 zc[s]=sum[j]; 70 s++;j++; 71 } 72 else{ 73 zc[s]=sum[i]; 74 i++;s++; 75 } 76 } 77 while(i<=mid) 78 { 79 zc[s]=sum[i]; 80 i++;s++; 81 } 82 while(j<=r1) 83 { 84 zc[s]=sum[j]; 85 j++;s++; 86 } 87 for(int i=l1;i<=r1;++i) 88 sum[i]=zc[i]; 89 } 90 void gcd(ll a,ll b,ll &gc) 91 { 92 if(!b) 93 { 94 gc=a; 95 return; 96 } 97 gcd(b,a%b,gc); 98 } 99 int main() 100 { 101 freopen("jian.in","r",stdin); 102 freopen("jian.out","w",stdout); 103 scanf("%d%d%d",&n,&l,&r); 104 for(int i=1;i<=n;++i) 105 a[i]=read(); 106 ll ans=0; 107 sum[0]=0; 108 for(int i=1;i<=n;++i) 109 sum[i]=a[i]-r; 110 for(int i=1;i<=n;++i) 111 sum[i]+=sum[i-1]; 112 gb1(0,n,ans); 113 sum[0]=0; 114 for(int i=1;i<=n;++i) 115 sum[i]=a[i]-l; 116 for(int i=1;i<=n;++i) 117 sum[i]+=sum[i-1]; 118 gb2(0,n,ans); 119 ll mu=(ll)(n+1)*n/2;/*一定要注意这个小细节,赋值时最大就是int,超过int,即使是给long long 赋值,也会炸数据类型*/ 120 ll gc; 121 gcd(ans,mu,gc); 122 ans/=gc;mu/=gc; 123 if(mu==1) printf("1"); 124 else cout<<ans<<"/"<<mu; 125 fclose(stdin); 126 fclose(stdout); 127 return 0; 128 }
T3:
考试时蒙蔽到连10%的数据都不会处理了。╮(╯▽╰)╭
1 /* 2 这道题目直接用dfs可以,也可以如下的先用dp处理一部分点。 3 但是都要用到状态压缩。 4 其实,这一点我们是应该想到的,每个栅栏内放哪几个葱是很重要的而且n<=16,我们应该想到用状压的。 5 */ 6 #include<cstdio> 7 #include<cstdlib> 8 #include<cstring> 9 #include<algorithm> 10 11 using namespace std; 12 13 int m,k,n,x[20],y[20],f[17][1<<16],cost[1<<16],s[20],ans; 14 15 void dfs(int now,int cnt,int res)/*res当前所用的栅栏长度,now当前处理第几根葱,cnt已用栅栏的数目*/ 16 { 17 if (now==n) 18 { 19 if (res<ans) ans=res; 20 return; 21 } 22 if (res+(k-cnt)*4>=ans) return;/*最优性剪枝,当前所用栅栏的数目+几乎最少的长度>=ans就剪枝*/ 23 /*对于当前这个葱,只有放入之前的栅栏和新建一个栅栏两种方案*/ 24 for (int a=1;a<=cnt;a++)/*枚举已用的栅栏*/ 25 { 26 int pres=s[a];/*取出这个栅栏的放的葱的情况*/ 27 s[a]|=(1<<now);/*把这个葱放进去,再进行下一步搜索*/ 28 dfs(now+1,cnt,res-cost[pres]+cost[s[a]]); 29 s[a]^=(1<<now);/*把这个葱取出来,回溯*/ 30 } 31 if (cnt<k) 32 { 33 cnt++;/*新建一个栅栏,把葱放进去*/ 34 s[cnt]=(1<<now); 35 dfs(now+1,cnt,res+4); 36 } 37 } 38 39 int main() 40 { 41 freopen("dan.in","r",stdin); 42 freopen("dan.out","w",stdout); 43 44 scanf("%d%d%d",&m,&k,&n); 45 for (int a=0;a<n;a++) 46 scanf("%d%d",&x[a],&y[a]); 47 if (n<=14) 48 { 49 memset(f,0x3f,sizeof(f)); 50 for (int a=1;a<(1<<n);a++)/*状态压缩,把葱在一个栅栏中的所有情况都枚举了出来*/ 51 { 52 int sx=1001,mx=-1,sy=1001,my=-1; 53 for (int b=0;b<n;b++) 54 if ((a>>b)&1) 55 { 56 if (x[b]<sx) sx=x[b]; 57 if (x[b]>mx) mx=x[b]; 58 if (y[b]<sy) sy=y[b]; 59 if (y[b]>my) my=y[b];/*计算这个栅栏的最小大小*/ 60 } 61 f[1][a]=(mx-sx+1)*2+(my-sy+1)*2; 62 } 63 for (int a=2;a<=k;a++)/*枚举k个栅栏*/ 64 for (int b=0;b<(1<<n);b++)/*枚举b个葱组成集合的1<<n中放置方法*/ 65 for (int c=b-1;c;c=(c-1)&b)/*这是一种状态压缩枚举b集合中的所有子集的方法,c=b-1,就可以表示所有的元素都在集合中,每次-1,可以枚举出1<<b的所有情况*/ 66 f[a][b]=min(f[a][b],f[a-1][c]+f[1][b^c]);/*枚举出任意子集c,b^c是c在b中的补集,c与b按位如果都有1或0,则他们的子集中都没有。*/ 67 int ans=f[1][(1<<n)-1];/*这是一个栅栏n个葱都放入的结果*/ 68 for (int a=2;a<=k;a++) 69 ans=min(ans,f[a][(1<<n)-1]); 70 printf("%d\n",ans); 71 } 72 else/*如果不这样特判的话,n>14会有超时*/ 73 { 74 ans=4000; 75 for (int a=1;a<(1<<n);a++) 76 { 77 int sx=1001,mx=-1,sy=1001,my=-1; 78 for (int b=0;b<n;b++) 79 if ((a>>b)&1) 80 { 81 if (x[b]<sx) sx=x[b]; 82 if (x[b]>mx) mx=x[b]; 83 if (y[b]<sy) sy=y[b]; 84 if (y[b]>my) my=y[b]; 85 } 86 cost[a]=(mx-sx+1)*2+(my-sy+1)*2;/*一个栅栏的情况*/ 87 } 88 dfs(0,0,0); 89 printf("%d\n",ans); 90 } 91 92 return 0; 93 }
我的:
1 #define M 1010 2 #include<iostream> 3 using namespace std; 4 #include<cstdio> 5 #define N 17 6 int n,m,k; 7 struct Pos{ 8 int x,y; 9 }pos[N]; 10 int val[1<<N],ans,zha[N+1]; 11 void input() 12 { 13 scanf("%d%d%d",&m,&k,&n); 14 for(int i=0;i<n;++i) 15 scanf("%d%d",&pos[i].x,&pos[i].y); 16 } 17 void pre_chuli() 18 { 19 for(int i=1;i<(1<<n);++i) 20 { 21 int dx=-1,dy=-1,xx=1001,xy=1001; 22 for(int j=0;j<n;++j) 23 { 24 if((1<<j)&i) 25 { 26 dx=max(dx,pos[j].x); 27 dy=max(dy,pos[j].y); 28 xy=min(xy,pos[j].y); 29 xx=min(xx,pos[j].x); 30 } 31 } 32 val[i]=(dy-xy+1)*2+(dx-xx+1)*2; 33 } 34 } 35 void dfs(int now,int cnt,int cos) 36 { 37 if(now==n) 38 { 39 ans=min(cos,ans); 40 return; 41 } 42 if(cos+(k-cnt)*4>=ans) return; 43 for(int i=1;i<=cnt;++i) 44 { 45 int x=zha[i]; 46 zha[i]|=(1<<now); 47 dfs(now+1,cnt,cos-val[x]+val[zha[i]]); 48 zha[i]^=(1<<now); 49 } 50 if(cnt<k) 51 { 52 zha[cnt+1]=(1<<now); 53 dfs(now+1,cnt+1,cos+4); 54 zha[cnt+1]=0; 55 } 56 } 57 int main() 58 { 59 freopen("dan.in","r",stdin); 60 freopen("dan.out","w",stdout); 61 input(); 62 pre_chuli(); 63 ans=4000; 64 dfs(0,0,0); 65 printf("%d\n",ans); 66 fclose(stdin); 67 fclose(stdout); 68 return 0; 69 }