排列组合专题

【Bzoj】1227: [SDOI2009]——虔诚的墓主人

Time Limit: 5 Sec Memory Limit: 259 MB

Description

小W 是一片新造公墓的管理人。公墓可以看成一块N×M 的矩形,矩形的每个格点,要么种着一棵常青树,要么是一块还没有归属的墓地。当地的居民都是非常虔诚的基督徒,他们愿意提前为自己找一块合适墓地。为了体现自己对主的真诚,他们希望自己的墓地拥有着较高的虔诚度。一块墓地的虔诚度是指以这块墓地为中心的十字架的数目。一个十字架可以看成中间是墓地,墓地的正上、正下、正左、正右都有恰好k 棵常青树。小W 希望知道他所管理的这片公墓中所有墓地的虔诚度总和是多少

Input

第一行包含两个用空格分隔的正整数N 和M,表示公墓的宽和长,因此这个矩形公墓共有(N+1) ×(M+1)个格点,左下角的坐标为(0, 0),右上角的坐标为(N, M)。第二行包含一个正整数W,表示公墓中常青树的个数。第三行起共W 行,每行包含两个用空格分隔的非负整数xi和yi,表示一棵常青树的坐标。输入保证没有两棵常青树拥有相同的坐标。最后一行包含一个正整数k,意义如题目所示。

Output

包含一个非负整数,表示这片公墓中所有墓地的虔诚度总和。为了方便起见,答案对2,147,483,648 取模。

Sample Input

5 6
13
0 2
0 3
1 2
1 3
2 0
2 1
2 4
2 5
2 6
3 2
3 3
4 3
5 2
2

Sample Output

6

HINT

图中,以墓地(2, 2)和(2, 3)为中心的十字架各有3个,即它们的虔诚度均为3。其他墓地的虔诚度为0。
所有数据满足1 ≤ N, M ≤ 1,000,000,000,0 ≤ xi ≤ N,0 ≤ yi ≤ M,1 ≤ W ≤ 100,000, 1 ≤ k ≤ 10。存在50%的数据,满足1 ≤ k ≤ 2。存在25%的数据,满足1 ≤ W ≤ 10000。
注意:”恰好有k颗树“,这里的恰好不是有且只有,而是从>=k的树中恰好选k棵

刚看到恰好k颗的时候很是不解,后来看到hint的时候才知道所谓的恰好k颗竟然指的是从>=k的树中恰好选k棵,。。。Orz。。。

那么对于一个确定的墓地,如果我们已经知道在他的上下左右的常青树的个数为l,r,d,u。那么他的虔诚度为

C(lk)C(rk)×C(uk)C(dk)
但是墓地的个数太多,无法枚举,但是常青树的个数却比较的少,我们从输入数据下手。

我们将常青树的坐标按照想x,y排序,对于一个常青树 xi,yi ,已知在同一横坐标中的树的数量和在他左侧数的数量(我们可以预处理出来),那么在他和左边的树之间的墓碑在横坐标的常青树的组合是一样的为

C(numk)×C(num[x]numk)
对于中间的墓碑的上下的组合为
s=yi1yi1C(upsk)×C(num[s]upsk)
那么总的虔诚度为
C(numk)×C(num[x]numk)×s=yi1yi1C(upsk)×C(num[s]upsk)
由于中间的墓碑也不能暴力,所以我们用树状数组维护区间和,再更新的时候,更新的值为
C(upy+1k)×C(num[y]upy1k)C(upyk)×C(num[y]upyk)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int Max =  110000;
const LL Mod = 1LL<<31;
struct node {
    LL x,y;
    bool operator < (const node &a)const {
        return (x==a.x)?y<a.y:x<a.x;
    }
} p[Max];
LL c[Max][15],Tr[Max];
LL ux[Max],uy[Max];
int numr[Max],numc[Max];
int num[Max];
LL n,m,k,w;
void Init() {
    c[0][0] = 1;
    for(int i =1; i<Max; i++) {
        c[i][0] =1;
        for(int j = 1; j<=13; j++) c[i][j] =(c[i-1][j]+c[i-1][j-1])%Mod;
    }
}
LL C(LL n, LL m) {
    if(m>n)  return 0;
    return c[n][m];
}
int lowbite(int x) {
    return x&(-x);
}
void upd(int x,LL d) {
    d =(d%Mod+Mod)%Mod;
    while(x<Max) {
        (Tr[x]+=d)%=Mod;
        x+=lowbite(x);
    }
}
LL que(int x) {
    LL ans = 0;
    while(x>0) {
        (ans+=Tr[x])%=Mod;
        x-=lowbite(x);
    }
    return ans;
}
int Lower_bound(int L,int R,LL aim,LL *a) {
    while(L<=R) {
        int mid = (L+R)>>1;
        if(aim == a[mid]) return mid;
        if(a[mid] < aim) L = mid+1;
        else R = mid-1;
    }
    return 0;
}
int main() {
    Init();
    while(~scanf("%lld %lld",&n,&m)) {
        scanf("%lld",&w);
        ux[0] = 0;
        uy[0] = 0;
        for(int i = 0; i<w; i++) {
            scanf("%lld %lld",&p[i].x,&p[i].y);
            ux[++ux[0]] = p[i].x;
            uy[++uy[0]] = p[i].y;
        }
        scanf("%lld",&k);
        sort(p,p+w);
        sort(ux+1,ux+1+ux[0]);
        sort(uy+1,uy+1+uy[0]);
        int s = ux[0];
        ux[0] =1;
        numr[1] =1;
        for(int i = 2; i<=s; i++) {
            if(ux[i]!=ux[ux[0]]) {
                ux[++ux[0]] = ux[i];
                numr[ux[0]] = 1;
            } else numr[ux[0]] ++;
        }
        s = uy[0];
        uy[0] = 1;
        numc[1] =1;
        for(int i = 2; i<=s; i++) {
            if(uy[i]!=uy[uy[0]]) {
                uy[++uy[0]] = uy[i];
                numc[uy[0]] = 1;
            } else numc[uy[0]] ++;
        }
        memset(Tr,0,sizeof(Tr));
        memset(num,0,sizeof(num));
        LL ans = 0;
        LL tep = 1;
        for(int i = 0; i<w; i++) {
            int Fx = Lower_bound(1,ux[0],p[i].x,ux);
            int Fy = Lower_bound(1,uy[0],p[i].y,uy);
            if(!i || p[i].x !=p[i-1].x) tep = 1;
            else tep++;
            if(tep != 1) {
                int Prey =  Lower_bound(1,uy[0],p[i-1].y,uy);
                LL ant = ((C(tep-1,k)*C(numr[Fx]-tep+1,k)%Mod)*((que(Fy-1)-que(Prey))%Mod))%Mod;
                ans = (ans+ant)%Mod;
            }
            upd(Fy,(C(num[Fy]+1,k)*C(numc[Fy]-num[Fy]-1,k))%Mod-(C(num[Fy],k)*C(numc[Fy]-num[Fy],k))%Mod);
            num[Fy]++;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

[NOIP2011]多项式系数

题目描述

(ax+by)k 的展开中 xnym 项的系数。由于系数可能很大,只要求输出除以 10007 的余数。

输入

一行共五个整数,分别为 a,b,k,n,m

输出

一个整数,为该项系数除以10007的余数。

样例输入

1 1 3 1 2

样例输出

3

数据范围:

30% 0<=k<=10,

50% a=1,b=1

100% 0<=k<=1000, 0<=n,m<=k 且 n+m=k, 0<=a,b<=100,000

NOIP2011 DAY2 factor

来源

NOIP2011

简单的组合 ans=C(kn)anbm

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL Mod = 10007;
const int Max = 200010;
LL fac[Max],facv[Max];
LL Pow(LL n, LL m) {
    LL ans = 1;
    while(m) {
        if(m&1) ans = (ans*n)%Mod;
        n = (n*n)%Mod;
        m>>=1;
    }
    return ans;
}
void Init(){
    fac[0] = 1; facv[0] = 1;
    for(int i =1;i<Max;i++) {
        fac[i] = (fac[i-1]*i)%Mod;
        facv[i] = Pow(fac[i],Mod-2);
    }
}
LL C(LL n,LL m) {
    if(m>n) return 0;
    return  ((fac[n]*facv[m])%Mod*facv[n-m])%Mod;
}
int  a,b,k,n,m;
int main(){
    Init();
    while(~scanf("%d %d %d %d %d",&a,&b,&k,&n,&m)){
        LL ans = ((C(k,n)*Pow(a,n))%Mod*Pow(b,m))%Mod;
        printf("%d\n",(int)ans);
    }
    return 0;
}

【Bzoj】3505: [Cqoi2014]——数三角形

Time Limit: 10 Sec Memory Limit: 128 MB

Description

给定一个nxm的网格,请计算三点都在格点上的三角形共有多少个。下图为4x4的网格上的一个三角形。

注意三角形的三点不能共线。

Input

输入一行,包含两个空格分隔的正整数m和n。

Output

输出一个正整数,为所求三角形数量。

Sample Input

2 2

Sample Output

76

数据范围

1<=m,n<=1000

对于不考虑共线的情况,总的方案数为 C(nm3) ,那么我们去除共线的情况,我们枚举贡共线的两端,然后将线段进行平移就可以计算出共线的个数。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int Max = 1000100;
LL c[Max][4];
void Init(){
    c[0][0] = 1;
    for(int i =1;i<Max;i++) {
        c[i][0] =1;
        for(int j = 1;j<=3;j++) {
            c[i][j] = c[i-1][j]+c[i-1][j-1];
        }
    }
}
LL C(LL n,LL m) {
    if(m>n) return 0;
    return c[n][m];
}
int n,m;
int main()
{
    Init();
    while(~scanf("%d %d",&n,&m)) {
        n++,m++;
        LL ans = C(n*m,3); ans = ans-C(n,3)*m-C(m,3)*n;
        for(int i = 1;i<n;i++) {
            for(int j = 1;j<m;j++) {
                int g = __gcd(i,j)+1;
                ans-=((g-2)*(n-i)*(m-j)*2);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

你可能感兴趣的:(排列组合专题)