team2012-E1-mysol-0002

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

{{{
/* 解题报告 //

给出 n 本书,要求每天恰好读完一本书,求读书顺序的方案总数
但是有 m 个特殊条件:在第 D[i] 天里无法读完第 C[i] 本书

我的做法是枚举这 m 条特殊条件中,违反了其中的哪些特殊条件
这么做其实就是用了容斥原理和德摩根定律:
不违反条件_1 and 不违反条件_2 and ... and 不违反条件_n
  = not (违反条件_1 or 违反条件_2 or ... or 违反条件_n)
  = 无条件的方案数 - count(违反 1 个条件的) + count(违反 2 个条件的)
  - ... + (-1) ^ n * count(违反 n 个条件的)

枚举时我用的暴力枚举的方式,对 2 ^ m 种可能的条件组合的都进行枚举
如果这种组合是可以出现的(不相互冲突),则参与计算
我在暴力枚举时用了一点小的代码技巧,使得不需要多出额外的 O(n) 时间
具体方法难以简单地描述清楚,大家可以看我的代码

用 dfs 来做的话会更快,因为有许多无效状态在 dfs 时可以被提前剪掉

// By 猛犸也钻地 @ 2012.07.18 */
}}}

{{{
#include <cstdio>
#include <algorithm>
using namespace std;

const int MOD = 55566677;

int main(){
    int n,m,P[51]={1},x[25],y[25];
    for(long long i=1;i<51;i++) P[i]=P[i-1]*i%MOD;
    while(scanf("%d%d",&n,&m)==2){
        int day[50]={},use[50]={};
        for(int i=0,j;i<m;i++){
            scanf("%d%d",x+i,y+i);
            x[i]--,y[i]--;
            for(j=0;j<i;j++) if(x[i]==x[j] && y[i]==y[j]) break;
            if(j<i) i--,m--;
        }
        int mask=(1<<m)-1,s=0,cnt=0,err=0,ans=P[n];
        for(int r=1;r<=mask;r++){
            int i=__builtin_ctz(r);
            s^=1<<i;
            if(1<<i&s){
                cnt++;
                if(day[x[i]]++) err++;
                if(use[y[i]]++) err++;
            }else{
                cnt--;
                if(--day[x[i]]) err--;
                if(--use[y[i]]) err--;
            }
            if(!err) ans=(ans+(cnt&1?MOD-P[n-cnt]:P[n-cnt]))%MOD;
        }
        printf("%d\n",ans);
    }
}
}}}
/* 解题报告 //
给出 n 本书,要求每天恰好读完一本书,求读书顺序的方案总数
但是有 m 个特殊条件:在第 D[i] 天里无法读完第 C[i] 本书
我的做法是枚举这 m 条特殊条件中,违反了其中的哪些特殊条件
这么做其实就是用了容斥原理和德摩根定律:
不违反条件_1 and 不违反条件_2 and ... and 不违反条件_n
  = not (违反条件_1 or 违反条件_2 or ... or 违反条件_n)
  = 无条件的方案数 - count(违反 1 个条件的) + count(违反 2 个条件的)
  - ... + (-1) ^ n * count(违反 n 个条件的)
枚举时我用的暴力枚举的方式,对 2 ^ m 种可能的条件组合的都进行枚举
如果这种组合是可以出现的(不相互冲突),则参与计算
我在暴力枚举时用了一点小的代码技巧,使得不需要多出额外的 O(n) 时间
具体方法难以简单地描述清楚,大家可以看我的代码
用 dfs 来做的话会更快,因为有许多无效状态在 dfs 时可以被提前剪掉
// By 猛犸也钻地 @ 2012.07.18 */
#include <cstdio>
#include <algorithm>
using namespace std;
const int MOD = 55566677;
int main(){
    int n,m,P[51]={1},x[25],y[25];
    for(long long i=1;i<51;i++) P[i]=P[i-1]*i%MOD;
    while(scanf("%d%d",&n,&m)==2){
        int day[50]={},use[50]={};
        for(int i=0,j;i<m;i++){
            scanf("%d%d",x+i,y+i);
            x[i]--,y[i]--;
            for(j=0;j<i;j++) if(x[i]==x[j] && y[i]==y[j]) break;
            if(j<i) i--,m--;
        }
        int mask=(1<<m)-1,s=0,cnt=0,err=0,ans=P[n];
        for(int r=1;r<=mask;r++){
            int i=__builtin_ctz(r);
            s^=1<<i;
            if(1<<i&s){
                cnt++;
                if(day[x[i]]++) err++;
                if(use[y[i]]++) err++;
            }else{
                cnt--;
                if(--day[x[i]]) err--;
                if(--use[y[i]]) err--;
            }
            if(!err) ans=(ans+(cnt&1?MOD-P[n-cnt]:P[n-cnt]))%MOD;
        }
        printf("%d\n",ans);
    }
}