【学习笔记】反射容斥

基本公式

最平凡

考虑平凡的网格图计数,从 \((0,0)\) 往右或往上走,走到 \((n,m)\) 方案数为 \(\binom{n+m}{n}\)

较平凡

若不经过直线 \(y = x+b\),直接在第一次经过这个直线的地方翻折,如下图。

\(A\to G\to C\) 翻转成 \(A \to G \to I\),这些都是一一对应的。

方案就减去 \((0,0)\to (m-b,n+b)\) 的方案数 \(\binom{n+m}{n}-\binom{m+n}{m-b}\)

平凡

考虑不经过 \(y=x+b,y=x+c\) 两条直线,设两条直线为 \(B,C\)

把一条路径表示为依次穿过的路径,如 \(BBCCB\),考虑到多次穿过一条直线只用保留一条,就变成 \(BCBCBC,CBCBCB\)

直接开始容斥,\(ans=\empty -B-C+BC+CB-BCB-CBC+BCBC+CBCB...\)

可以分为 \(BCBC\)\(CBCB\) 统计,每次穿过一条直线把目标点关于这条直线对称即可。

复杂度 \(\Theta(\dfrac{n+m}{|b-c|})\)

例题

P3266

题目链接

考虑每行 \(a_{i,j}<a_{i,j+1}\),且 \(a_{i,j} \in [0,m],j\in [1,m]\),所以每行中有且仅有一个 \([0,m]\) 的数不出现。

\(f_{i,j}\)\(i\)\(j\) 不出现方案数。

\(f_{i,j} = \sum\limits_{k=0}^{j+1} f_{i-1,k}\)

等价于 \(f_{i,j}=f_{i,j-1}+f_{i-1,j+1}\),答案为 \(f_{n+1,m}\)

组合意义是从 \((1,0)\) 出发到 \((n+1,m)\),每次往右或往左上,当 \(x=1\) 时可以往上的方案数,转动 \(45\degree\),发现是从 \((0,0)\) 走到 \((n+m+1,n)\) 且不经过 \(y=x+1,y=x-m-2\) 的方案,按照上面的方法做就行。

#include <bits/stdc++.h>
#define _rep(i, x, y) for(int i = x; i <= y; ++i)
#define _req(i, x, y) for(int i = x; i >= y; --i)
#define _rev(i, u) for(int i = head[u]; i; i = e[i].nxt)
#define pb push_back
#define fi first
#define se second
#define mst(f, i) memset(f, i, sizeof f)
using namespace std;
#ifdef ONLINE_JUDGE
#define debug(...) 0
#else
#define debug(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)
#endif
typedef long long ll;
typedef pair<int, int> PII;
namespace fastio{
    char ibuf[50007],*p1 = ibuf, *p2 = ibuf;
    #ifdef ONLINE_JUDGE
    #define get() p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, 50007, stdin), p1 == p2) ? EOF : *p1++
    #else
    #define get() getchar()
    #endif
    template<typename T> inline void read(T &t){
        T x = 0, f = 1;
        char c = getchar();
        while(!isdigit(c)){
            if(c == '-') f = -f;
            c = getchar();
        }
        while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
        t = x * f;
    }
    template<typename T, typename ... Args> inline void read(T &t, Args&... args){
        read(t);
        read(args...);
    }
    template<typename T> void write(T t){
        if(t < 0) putchar('-'), t = -t;
        if(t >= 10) write(t / 10);
        putchar(t % 10 + '0');
    }
    template<typename T, typename ... Args> void write(T t, Args... args){
        write(t), putchar(' '), write(args...);
    }
    template<typename T> void writeln(T t){
        write(t);
        puts("");
    }
    template<typename T> void writes(T t){
        write(t), putchar(' ');
    }
    #undef get
};
using namespace fastio;
#define multitest() int T; read(T); _rep(tCase, 1, T)
namespace Calculation{
    const ll mod = 1e9 + 7;
    ll ksm(ll p, ll h){ll base = p % mod, res = 1; while(h){if(h & 1ll) res = res * base % mod; base = base * base % mod, h >>= 1ll;} return res;}
    void dec(ll &x, ll y){x = ((x - y) % mod + mod) % mod;}
    void add(ll &x, ll y){x = (x + y) % mod;}
    void mul(ll &x, ll y){x = x * y % mod;}
    ll sub(ll x, ll y){return ((x - y) % mod + mod) % mod;}
    ll pls(ll x, ll y){return ((x + y) % mod + mod) % mod;}
    ll mult(ll x, ll y){return x * y % mod;}
}
using namespace Calculation;
const int N = 4e6 + 5;
ll n, m, pw[N], inv[N];
ll C(ll n, ll m){
    if(n < m || m < 0) return 0;
    if(n == m || !m) return 1;
    return pw[n] * inv[m] % mod * inv[n - m] % mod;
}
ll b, c, x, y;
ll calc(ll x, ll y){return C(x + y, x);}
void work(ll &x, ll &y, ll b){
    ll tx = x, ty = y;
    y = tx + b, x = ty - b;
}
void work(ll &b, ll &c){
    b = 2 * c - b;
}
int main(){
    read(n, m), pw[1] = inv[1] = 1;
    _rep(i, 2, 4e6) pw[i] = pw[i - 1] * i % mod, inv[i] = (-mod / i + mod) * inv[mod % i] % mod;
    _rep(i, 2, 4e6) inv[i] = inv[i - 1] * inv[i] % mod;
    b = 1, c = -m - 2, x = n + m + 1, y = n;
    ll ans = calc(x, y);
    for(int i = 1; x >= 0 && y >= 0; ++i){
        if(i & 1){
            work(x, y, b), dec(ans, calc(x, y));
            work(c, b);
        }else{
            work(x, y, c), add(ans, calc(x, y));
            work(b, c);
        }
    }
    b = 1, c = -m - 2, x = n + m + 1, y = n;
    for(int i = 1; x >= 0 && y >= 0; ++i){
        if(i & 1){
            work(x, y, c), dec(ans, calc(x, y));
            work(b, c);
        }else{
            work(x, y, b), add(ans, calc(x, y));
            work(c, b);
        }
    }
    writeln(ans);
    return 0;
}

GYM104053J

题目链接

\(4s_i=(a_i +1)^2\)\(4s_{i-1} = (a_i-1)^2\),即 \(a_i = 1\pm 2\sqrt{s_{i-1}}\)

\(s_i=s_{i-1} -2\sqrt{s_{i-1}}+1=(\sqrt{s_{i-1}} -1)^2\)\(s_i = s_{i-1} + 2\sqrt{s_{i-1}}+1=(\sqrt{s_{i-1}}+1)^2\)。考虑二元组 \((i,\sqrt{s_i})\),可以走到 \((i,\sqrt{s_i}+1),(i,\sqrt{s_i}-1)\)

同样旋转一下,又因为 \(|a_i|\le m\),所以转化为从 \((0,0)\) 出发,不经过直线 \(y=x+\dfrac{m+1}{2}+1,y=x-1\),最后终点在 \(y=-x+n\) 上的方案。

可以直接枚举终点,复杂度 \(\Theta(b-c)\),乘上容斥复杂度就是 \(\Theta(n+m)\)

posted @ 2024-07-03 22:17  FantasyNumber  阅读(1)  评论(0编辑  收藏  举报