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 }

 

posted @ 2016-11-06 18:55  csgc0131123  阅读(479)  评论(0编辑  收藏  举报