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 }
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 }
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 }
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)就是答案了