首页 > 其他分享 >容斥

容斥

时间:2023-08-16 18:25:40浏览次数:22  
标签:int idt 位置 容斥 pos 枚举

牛客09 F (优化DP状态)

有一个思路比较顺的DP:按照num一个个考虑,记录当前每个位置是否已填,以及B数组剩余的状态;然后枚举新增的位置,判断合法性转移。容易发现B数组剩余的状态数不多,手模几个情况卡不到100(实际上最多72);瓶颈在于转移,如果直接枚举新增的集合,总复杂度是\(O(72m3^n)\),可以用每次新增一个的方法优化枚举子集,但这样得记录新增的个数以及上一个新增的位置,总复杂度是\(O(72mn^22^n)\),都难以通过。
发现按照上面的思路已经难以优化了,即同时记录72和\(2^n\)过于暴力了!考虑优化这个\(2^n\):我们要记这么多,为的就是满足每个位置恰好只填一个数这个比较严格的限制;能否将其放宽?考虑容斥!由于把num全部考虑完后,总共填了n次,所以只需考虑每个位置都填过或者每个位置被填不超过一次这两个等价表达中的一个,而容斥的话,肯定是对前者容斥更容易,即枚举至少没有被填过的位置集合,问题就变成在一个位置子集内填数,那DP的时候就只要记录72个状态,用组合数算算转移即可!总复杂度是\(O(72mn2^n)\)

点击查看代码
#include "bits/stdc++.h"
using namespace std;
const int P=1e9+7;
void inc(int& x,int y){
    x+=y;
    if(x>=P) x-=P;
}
int prd(int x,int y){
    return 1ll*x*y%P;
}
const int N=35,M=105;
int c[N][N];
int C(int n,int m){
    if(m<0 || m>n) return 0;
    return c[n][m];
}
int n,m,a[N],cnt[N],tot;
bool A[N][N],B[N][N];
int idt,tt;
struct edge{
    int v,d;
};
vector<edge>to[M];
map< vector<int>,int>mp;
void dfs(vector<int>res,int pos){
    mp[res]=++idt;
    int id=idt;
    bool pd=0;
    for(auto u:res) if(u) pd=1;
    if(!pd) tt=id;
    for(int j=pos+1;j<tot;j++) if(res[j]){
        res[j]--;
        dfs(res,j);
        res[j]++;
    }
    if(res[pos]){
        res[pos]--;
        dfs(res,pos);
        res[pos]++;
    }
}
void add(vector<int>res,int pos){
    for(int j=pos+1;j<tot;j++) if(res[j]){
        res[j]--;
        add(res,j);
        res[j]++;
    }
    if(res[pos]){
        res[pos]--;
        add(res,pos);
        res[pos]++;
    }
    //for(auto u:res) cout<<u<<" ";
    int id=mp[res];
    //cout<<"pos="<<pos<<" id="<<id<<endl;
    for(int i=0;i<tot;i++) if(res[i]){
        res[i]--;
        to[id].push_back((edge){mp[res],i+1});
        //cout<<id<<" "<<mp[res]<<" "<<i+1<<endl;
        res[i]++;
    }
    to[id].push_back((edge){id,0});
}
int pw[N],c1[1<<16];
void solve() {
    memset(A,0,sizeof(A));
    memset(B,0,sizeof(B));
    for(int i=0;i<M;i++) to[i].clear();
    int x,y;
    cin>>n>>m>>x>>y;
    int lst=0,c0=0;
    tot=0;
    for(int u,i=1;i<=m;i++){
        scanf("%d",&u);
        if(!u){
            c0++;
            continue;
        }
        if(u==lst) cnt[tot]++;
        else a[++tot]=u,cnt[tot]=1,lst=u;
    }
    //cout<<"tot="<<tot<<endl;
    //for(int i=1;i<=tot;i++) cout<<a[i]<<" "<<cnt[i]<<endl;

    while(x--){
        int u,v;
        cin>>u>>v;
        A[u][v]=1;
    }
    while(y--){
        int u,v;
        cin>>u>>v;
        B[u][v]=1;
    }

    tt=idt=0; mp.clear();
    vector<int>rest;
    for(int i=1;i<=tot;i++) rest.push_back(cnt[i]);
    dfs(rest,0);
    add(rest,0);
    int ans=0;
    for(int s2=1;s2<pw[n];s2++){
        int f[N][M]={0};
        f[0][1]=1;
        for(int i=1;i<=m;i++){ //num
            int vld=0;
            for(int j=1;j<=n;j++) if(s2&pw[j-1] && !A[j][i]) vld++;
            //cout<<"i="<<i<<" vld="<<vld<<endl;
            for(int s1=1;s1<=idt;s1++) if(f[i-1][s1]){
                //cout<<"s1="<<s1<<endl;
                for(auto u:to[s1]) if(!B[i][a[u.d]]){
                    //cout<<"d="<<u.d<<" v="<<u.v<<" a="<<a[u.d]<<endl;
                    inc(f[i][u.v],prd(f[i-1][s1],C(vld,a[u.d])) );
                }
            }
        }
        int op=1;
        for(int i=0;i<n;i++) if(!(s2&pw[i])) op=P-op;
        inc(ans,prd(op,f[m][tt]));
    }
    cout<<ans<<endl;
}

signed main() {
//	freopen("in","r",stdin);
//	freopen("out","w",stdout);

    for(int i=0;i<=16;i++){
        pw[i]=(1<<i);
        c[i][0]=1;
        if(!i) continue;
        for(int j=1;j<=i;j++) c[i][j]=c[i-1][j-1],inc(c[i][j],c[i-1][j]);
    }
    for(int i=0;i<pw[16];i++){
        for(int j=0;j<16;j++){
            if(pw[j]&i) c1[i]++;
        }
    }
	int T;cin >> T;while( T-- ) solve();
//	solve();
}

标签:int,idt,位置,容斥,pos,枚举
From: https://www.cnblogs.com/szsz/p/17635882.html

相关文章

  • [数论第四节]容斥原理/博弈论/NIM游戏
    容斥原理\(|A\cupB\cupC|=|A|+|B|+|C|-|A\capB|-|A\capC|-|B\capC|+|A\capB\capC|\)\(|\displaystyle\cup_{i=1}^nA_i|=\sum_{i}|A_i|-\sum_{i,j}|A_i\capA_j|+\ldots+(-1)^{n+1}|\cap_{i=1}^nA_i|\)时间复杂度:\(C_n^1+C_n^2+C_n^3···+C_n^n=2......
  • 广义容斥定理杂谈
    概念用语言描述,容斥原理求的是不满足任何性质的方案数,我们通过计算所有至少满足\(k\)个性质的方案数之和来计算。同样的,我们可以通过计算所有至少满足\(k\)个性质的方案数之和来计算恰好满足\(k\)个性质的方案数。这样的容斥方法我们称之为广义容斥原理。......
  • 容斥原理:能被整除的数
    给定一个整数 <spanid="MathJax-Span-2"class="mrow"><spanid="MathJax-Span-3"class="mi">n 和 <spanid="MathJax-Span-5"class="mrow"><spanid="MathJax-Span-6"class="......
  • 容斥原理
    Part1:知识点Part2:例题【模板题】区间整除数题意给出一个数组\(a[1..n]\),问在区间\([L,R]\)中有多少个数,至少能被a中的一个数整除。解题思路总体来说,我们可先求出区间\([1,L-1]\)中能被a数组整除的数,再求出\([1,R]\)中能被a数组整除的数,两者相减即是答案那么对于......
  • 【题解】[ABC312G] Avoid Straight Line(容斥,树上统计,dfs)
    【题解】[ABC312G]AvoidStraightLine题目链接[ABC312G]AvoidStraightLine题意概述给定一棵\(n\)个节点的树,第\(i\)条边连接节点\(a_i\)和\(b_i\),要求找到满足以下条件的三元整数组\((i,j,k)\)的数量:\(1\lei<j<k\len\);对于树上任意一条简单路径,都不同时经......
  • 容斥原理
    容斥原理内容用于解决多个有相交情况的集合的并集,例如三个集合的情形:对于n个集合的交集有公式:\(|S_1\cupS_2\cupS_3\cup\dotsS_n|=(|S_1|+|S_2|+|S_3|+\dots+|S_n|)-(|S_1\capS_2|+|S_1\capS_3|+\dots+|S_{n-1}\capS_{n}|+(|S_1\capS_2\capS_3|+|S_1\capS_2\cap......
  • 容斥原理
     容斥原理的原式有两个,分别是第一形式:|AUB|=|A|+|B|-|AB|                          第二形式:|AUBUC|=|A|+|B|+|C|-|AB|-|AC|-|BC|+|ABC|容斥原理最经典的应用是与dp相结合下面给出一道例题:P1450[HAOI2008]硬币......
  • CF1585F Non-equal Neighbours - 容斥 - dp - 单调栈
    题目链接:https://codeforces.com/problemset/problem/1585/F题解:难难难考虑容斥:设\(A_i\)表示\(b_i\neqb_{i+1}\)(\(i=1,2,\cdots,n-1\))时对应的\(\{b_i\}\)方案的答案那么答案就是$$\bigcap_{{i=1}}^{n}A_{i}=|U|-\left|\bigcup_{i=1}^n\overline{A_i}\right|$$......
  • BZOJ 1042:[HAOI2008]硬币购物 容斥原理 背包dp
    1042:[HAOI2008]硬币购物TimeLimit: 10Sec  MemoryLimit: 162MBSubmit: 2505  Solved: 1505[Submit][Status][Discuss]Description硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买si的价值的东西。请问每次......
  • 【题解】[ABC304F] Shift Table(容斥)
    【题解】[ABC304F]ShiftTable题目链接ABC304F题意概述Takahashi和Aoki将在接下来的\(N\)天里兼职工作。Takahashi这\(n\)天的出勤情况由字符串\(S\)表示,其中\(S\)的第\(i\)个字符是#则表示他在第\(i\)天工作,第\(i\)个字符是.表示他在第\(i\)天休息......