[FJOI2017]矩阵填数 (容斥原理)

题目传送门
现在看来熊猫杯的J题原来是个容斥套路题,按照值域排序后根据值域划分方块数,枚举子集容斥计算即可。

#include<cstdio>
#include<algorithm>
using namespace std;
const int p = 1e9 + 7;
const int N = 1100;

struct Matrix{
    int x, y, x1, y1, v;
    
    bool check() { return (x <= x1) && (y <= y1); }
    int cal() { return (x1 - x + 1) * (y1 - y + 1); }
    void read() { scanf("%d%d%d%d%d", &x, &y, &x1, &y1, &v); }

    bool operator <(const Matrix &ret) { return v < ret.v; }
 
    void operator |(const Matrix &ret){
        x = max(x, ret.x),  y = max(y, ret.y);
        x1 = min(x1, ret.x1),  y1 = min(y1, ret.y1);
    }
}mat[15];

int h, w, n, m;
int s[N], u[N], num[N];

int qpow(int x, int k){
    int ans = 1;
    for(; k; k >>= 1){
        if(k & 1)  ans = 1LL * ans * x % p;
        x = 1LL * x * x % p; 
    }
    return ans;
}

void solve(){
    scanf("%d%d%d%d", &h, &w, &m, &n);
    for(int i = 1; i <= n; ++i)  mat[i].read();
    sort(mat + 1, mat + 1 + n);
    // 求交集
    for(int i = 1, up = 1 << n; i < up; ++i){
        Matrix tt = {1, 1, h, w, 0};
        for(int t = i, j = 1; t; t >>= 1, ++j){
            if(t & 1)  tt | mat[j];
        }
        if(tt.check())  s[i] = tt.cal();  
    }
    // 求并集
    for(int i = 1, up = 1 << n; i < up; ++i){
        for(int t = i; t; t = (t - 1) & i){
            if(num[t] & 1)  u[i] += s[t];
            else  u[i] -= s[t];  
        }
    }
    // 划分值域容斥计算
    // now是目前枚举的集合,pre是之前枚举过的集合
    int now = 0, pre = 0, ans = 1;
    for(int i = 1; i <= n; ++i){
        now |= (1 << (i - 1));
        if(mat[i].v == mat[i + 1].v)  continue;
        int res = u[now | pre] - u[pre];
        int sum = qpow(mat[i].v, res);
        for(int t = now; t; t = (t - 1) & now){
            int tot = u[t | pre] - u[pre];
            int del = 1LL * qpow(mat[i].v - 1, tot) * qpow(mat[i].v, res - tot) % p;
            if(num[t] & 1)  sum = (sum - del + p) % p;
            else  sum = (sum + del) % p; 
        }   
        ans = 1LL * ans * sum % p;
        pre |= now;
        now = 0;
    }
    printf("%d\n", 1LL * ans * qpow(m, h * w - u[(1 << n) - 1]) % p);
}

void clr(){
    for(int i = 0; i < N; ++i)  s[i] = u[i] = 0;
}

int main(){
    int T;
    scanf("%d", &T);
    for(int i = 1; i < N; ++i)  num[i] = num[i >> 1] + (i & 1);
    while(T--){
        solve();
        clr();
    }
}
posted @ 2020-12-29 10:13  のNice  阅读(71)  评论(0编辑  收藏  举报