20171015 杂题

BZOJ[4563] [Haoi2016]放棋子

这个题是由套路的,可以直接用错排公式,首先f[1]=0,f[2]=1,然后考虑后面的转移,当放第i个的时候,前面的只可能是i-1个全部都不在自己的位置或者只有1个不在自己的位置,对于前者,第i个可以和前i-1个中的任意一个互换位置,贡献就是f[i-1]*(i-1),对于后者,第i个只能和在自己位置上的互换位置,贡献就是f[i-2]*(i-1);,然后套一个高精就行了

 1 #include <cmath>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <iostream>
 6 #include <algorithm>
 7 using namespace std;
 8 int n;
 9 void init(){int x;for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&x);}
10 struct Bignum{
11     int num[6000],len;
12     Bignum(){memset(num,0,sizeof(num)); len=0;}
13     void clear(){memset(num,0,sizeof(num)); len=0;}
14     void pt(){
15         for(int i=len;i>=1;i--){
16             printf("%d",num[i]);
17         } printf("\n");
18     }
19 };
20 Bignum c;
21 Bignum operator * (const Bignum a,const int b){
22     c.clear(); int len=a.len;
23     for(int i=1;i<=len;i++){
24         c.num[i]+=a.num[i]*b;
25         c.num[i+1]+=c.num[i]/10;
26         c.num[i]%=10;
27     }
28     if(c.num[len+1]) len++;
29     while(c.num[len]>10){
30         c.num[len+1]+=c.num[len]/10;
31         c.num[len]%=10;
32         len++;
33     }
34     c.len=len; return c;
35 }
36 Bignum operator + (const Bignum a,const Bignum b){
37     c.clear(); int len=a.len+b.len;
38     for(int i=1;i<=len;i++){
39         c.num[i]+=a.num[i]+b.num[i];
40         c.num[i+1]+=c.num[i]/10;
41         c.num[i]%=10;
42     }
43     if(c.num[len+1]) len++;
44     while(!c.num[len]) len--;
45     c.len=len; return c;
46 }
47 Bignum f[2010];
48 void work(){
49     f[1].num[1]=0; f[1].len=1; f[2].num[1]=1;  f[2].len=1;
50     for(int i=3;i<=n;i++){
51         f[i]=(f[i-1]+f[i-2])*(i-1);
52     }
53     f[n].pt();
54 }
55 void tst(){
56     Bignum a,b;
57     a.len=1; a.num[1]=1;
58     a=a*1234; a.pt();
59     b=a; b=b+a; 
60     b.pt();
61 }
62 int main(){
63     scanf("%d",&n);
64     init();
65     work();
66     return 0;
67 }
View Code

 

BZOJ[3170] [Tjoi 2013]松鼠聚会

可以神奇的把这个切比雪夫距离转化为曼哈顿距离(之前学长好想提到过一次),具体的证明还是挺简单的 http://blog.csdn.net/slongle_amazing/article/details/50911504

 1 #include <cmath>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cstdlib>
 5 #include <iostream>
 6 #include <algorithm>
 7 # define maxn 100010
 8 using namespace std;
 9 typedef long long LL;
10 int n;
11 struct node {
12     LL x,y,sumx,sumy;
13     node(){sumx=0; sumy=0;}
14 }g[maxn];
15 void init(){
16     scanf("%d",&n);
17     int x,y;
18     for(int i=1;i<=n;i++){
19         scanf("%d%d",&x,&y);
20         g[i].x=x+y; g[i].y=x-y;
21 //        cout<<g[i].x<<"  "<<g[i].y<<endl;
22     }
23 }
24 LL sum[maxn];
25 bool cmp1(const node a,const node b){return a.x<b.x;}
26 bool cmp2(const node a,const node b){return a.y<b.y;}
27 void work(){
28     sort(g+1,g+n+1,cmp1);
29     memset(sum,0,sizeof(sum));
30     for(int i=1;i<=n;i++) sum[i]=sum[i-1]+(LL)g[i].x;
31     for(int i=1;i<=n;i++){
32         g[i].sumx+=g[i].x*(i-1)-sum[i-1];
33         g[i].sumx+=(sum[n]-sum[i])-(n-i)*g[i].x;
34     }
35     sort(g+1,g+n+1,cmp2);
36     memset(sum,0,sizeof(sum));
37     for(int i=1;i<=n;i++) sum[i]=sum[i-1]+g[i].y;
38     for(int i=1;i<=n;i++){
39         g[i].sumy+=g[i].y*(i-1)-sum[i-1];
40         g[i].sumy+=(sum[n]-sum[i])-(n-i)*g[i].y;
41     }
42     LL ans=0x7fffffffffffffffll;
43     for(int i=1;i<=n;i++){
44         ans=min(g[i].sumx+g[i].sumy,ans);
45     }
46     cout<<ans/2<<endl;
47 }
48 int main(){
49 //    freopen("a.in","r",stdin);
50     init();
51     work();
52     return 0;
53 }
View Code

BZOJ[1079] [SCOI2008]着色方案

这个题可以用 记忆化搜索+哈希+map 水过,就是把每次剩下的所有颜色的个数除了上一个的颜色之外排序,然后哈希记录一下当前的贡献,当下次再遇到这个状态的时候,直接return就行了

 1 #include <map>
 2 #include <cmath>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <cstdlib>
 6 #include <iostream>
 7 #include <algorithm>
 8 # define mod 1000000007
 9 # define P 76543
10 using namespace std;
11 typedef long long LL;
12 typedef unsigned long long ULL;
13 void ot(){cout<<"****"<<endl;}
14 void fen(){cout<<"----------------------------"<<endl;}
15 ULL px[22];
16 void beg(){px[0]=1; for(int i=1;i<=20;i++) px[i]=px[i-1]*P;}
17 int n,tot;
18 int Num[20];
19 map<ULL,LL> ma;
20 bool cmp(const int a,const int b){return a<b;}
21 int cun[15];
22 ULL HS(int x,int lst){
23     ULL ret=0;
24     for(int i=1;i<=x;i++){
25         ret+=cun[i]*px[i];
26     }
27     return ret;
28 }
29 LL dfs(int x,int lst){
30     if(x==tot+1){ return 1; }
31     int tip=0; ULL hs;
32     for(int i=1;i<=n;i++){
33         if(i==lst) continue;
34         cun[++tip]=Num[i];
35     }
36     sort(cun+1,cun+tip+1,cmp); cun[++tip]=Num[lst]; 
37     hs=HS(tip,lst);
38     if(ma.count(hs)){return ma[hs];}
39     LL now=0;
40     for(int i=1;i<=n;i++){
41         if(i==lst || !Num[i]) continue;
42         Num[i]--;
43         now+=dfs(x+1,i);
44         now%=mod;
45         Num[i]++;
46     }
47     ma[hs]=now;
48     return now;
49 }
50 int main(){
51 //  freopen("a.in","r",stdin);
52 //  freopen("ce.out","w",stdout);
53     scanf("%d",&n);
54     beg();
55     for(int i=1;i<=n;i++) scanf("%d",&Num[i]),tot+=Num[i];
56     LL ans=dfs(1,-1);
57     cout<<ans<<endl;
58     return 0;
59 }
View Code

BZOJ[2839] 集合计数

这个题就是可以先从n个里面选出K个,然后考虑剩下的元素的组成的集合,互没有交集的方案数,

开始我想的是用一个f数组来递推,然后利用挡板法来求,后来发现并不能这么转移,因为有很多重复的情况没有办法去掉,然而正解是之前的题上试过很多次的不行的容斥原理,

用f[i]表示剩下的元素组成交集大小至少是i的方案数,因为至少要选择i个,那就先乘一个C(n-K,i);然后剩下的(n-i-K)个元素有2^(n-i-K)个子集,每一个集合都可以选或者不选,但是不能都不选,因为当前的含义是把调出来的K个放在这些集合里,然后就能得到f[i]=C(n-K,i)*2^(2^(n-i-K)),然后偶加奇减,乘C(n,K)就是答案了

 

posted @ 2017-10-15 21:13  Nawox  阅读(128)  评论(0编辑  收藏  举报