CF1392I 题解

8 篇文章 0 订阅
8 篇文章 0 订阅

CF1392I Kevin and Grid

做这题的时候感觉网上的讲解很不清楚。但是写完了之后发现其实我也讲不出什么东西 … \dots

本质上这是一个联通块计数的问题。

我们考虑这个数据范围肯定不是给 d p , d f s dp, dfs dp,dfs 的。

考虑是一个网格图,有一定的性质。考虑一下欧拉定理。

设联通块的个数是 k k k

那么 V − E + F = k + 1 V - E + F = k + 1 VE+F=k+1

我们就可以通过这些求出联通块的个数。

但是不同的联通块会产生不同的贡献。我们在加上的联通块数量的贡献之后还需要加上包含在内部的贡献。

不妨设 ≥ x ​ \ge x​ x 的点构成的联通块是 A ​ A​ A,另一个是 B ​ B​ B

我们考虑网格图那么面(除了最外面的)肯定是四联通。或者是中间包含了一个 A A A 的联通块。

那么本质上 F B − B 中的 4 联通块 − 1 F_B - \text{B 中的 4 联通块} - 1 FB中的 4 联通块1 就是 A A A 中包含的联通块。

A A A 中的 4 4 4 联通块是 D A D_A DA B B B 中的是 D B D_B DB

那么最终的答案就是。
V A − E A + F A − V B + E B − F B + I n A − I n B = V A − E A − V B + E B + D A − D B \begin{aligned} &V_A - E_A + F_A - V_B + E_B - F_B + In_A - In_B \\ =&V_A - E_A - V_B + E_B + D_A - D_B \end{aligned} =VAEA+FAVB+EBFB+InAInBVAEAVB+EB+DADB
我们考虑如何统计这些信息。不妨拿 A A A 的信息举例子。

  • 4 4 4 联通块: 也就是 4 4 4 个位置都 ≥ x \ge x x,求 min ⁡ ( a i , a i − 1 ) + min ⁡ ( b j , b j − 1 ) ≥ x \min(a_i, a_{i - 1}) + \min(b_j, b_{j - 1}) \ge x min(ai,ai1)+min(bj,bj1)x 计算。
  • 边 : 对于横着的边求 min ⁡ ( b i , b i − 1 ) + a j ≥ x \min(b_i, b_{i - 1}) + a_j \ge x min(bi,bi1)+ajx 即可,竖着就是 min ⁡ ( a i , a i − 1 ) + b j ≥ x \min(a_i, a_{i - 1}) + b_j \ge x min(ai,ai1)+bjx
  • 点: 直接 F F T FFT FFT 即可。
#include <bits/stdc++.h>
using namespace std;

//#define Fread
//#define Getmod

#ifdef Fread
char buf[1 << 21], *iS, *iT;
#define gc() (iS == iT ? (iT = (iS = buf) + fread (buf, 1, 1 << 21, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
#define getchar gc
#endif // Fread

template <typename T>
void r1(T &x) {
	x = 0;
	char c(getchar());
	int f(1);
	for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(; '0' <= c && c <= '9';c = getchar()) x = (x * 10) + (c ^ 48);
	x *= f;
}

template <typename T,typename... Args> inline void r1(T& t, Args&... args) {
    r1(t);  r1(args...);
}

//#define int long long
const int lim = (1 << 18), len = 18;
const int N = lim;
const double pi = acos(-1.0);
const int maxn = N + 20;

struct Complex {
    double x, y;
    Complex(double a = 0, double b = 0) : x(a), y(b) {}
    Complex operator + (const Complex &z) const { return Complex(x + z.x, y + z.y); }
    Complex operator - (const Complex &z) const { return Complex(x - z.x, y - z.y); }
    Complex operator * (const Complex &z) const { return Complex(x * z.x - y * z.y, x * z.y + y * z.x); }
    Complex & operator += (const Complex &z) { return *this = *this + z, *this; }
    Complex & operator -= (const Complex &z) { return *this = *this - z, *this; }
    Complex & operator *= (const Complex &z) { return *this = *this * z, *this; }
}tmp[maxn];

Complex mnA[maxn], mnB[maxn];

Complex mxA[maxn], mxB[maxn];

Complex nA[maxn], nB[maxn];

int rev[maxn];

void FFT(Complex *A,int opt) {
    for(int i = 0; i < lim; ++ i) if(i < rev[i]) swap(A[i], A[rev[i]]);
    for(int mid = 1; mid < lim; mid <<= 1) {
        Complex wn(cos(pi / mid), opt * sin(pi / mid));
        for(int j = 0, c = (mid << 1); j < lim; j += c) {
            Complex W(1, 0);
            for(int k = 0; k < mid; ++ k, W *= wn) {
                Complex x = A[j + k], y = W * A[j + k + mid];
                A[j + k] = x + y;
                A[j + k + mid] = x - y;
            }
        }
    }
    if(opt == -1) for(int i = 0; i < lim; ++ i) A[i].x /= lim;
}

int n, m, Q;
typedef long long ll;
int a[maxn], b[maxn];

void Mul(ll *ans,Complex *A, Complex *B) {
    for(int i = 0; i < lim; ++ i) tmp[i] = A[i] * B[i];
    FFT(tmp, -1);
    for(int i = 0; i < lim; ++ i) ans[i] += round(tmp[i].x);
}

ll V[maxn << 1], mxE[maxn << 1], mnE[maxn << 1], mxD[maxn << 1], mnD[maxn << 1];



signed main() {
//    freopen("S.in", "r", stdin);
//    freopen("S.out", "w", stdout);
    int i, j;
    for(i = 0; i < lim; ++ i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (len - 1));
    r1(n, m, Q);
    for(i = 1; i <= n; ++ i) {
        r1(a[i]);
        nA[a[i]].x ++;
        if(i > 1)
            mnA[min(a[i - 1], a[i])].x ++,
            mxA[max(a[i - 1], a[i])].x ++;
    }
    for(i = 1; i <= m; ++ i) {
        r1(b[i]);
        nB[b[i]].x ++;
        if(i > 1)
            mnB[min(b[i - 1], b[i])].x ++,
            mxB[max(b[i - 1], b[i])].x ++;
    }
    FFT(nA, 1), FFT(nB, 1);
    FFT(mnA, 1), FFT(mxA, 1);
    FFT(mnB, 1), FFT(mxB, 1);

    Mul(V, nA, nB);
    for(i = 1; i <= N; ++ i) V[i] += V[i - 1];

    Mul(mxE, mnA, nB);
    Mul(mxE, nA, mnB);
    for(i = N; i >= 1; -- i) mxE[i] += mxE[i + 1];

    Mul(mnE, nA, mxB);
    Mul(mnE, mxA, nB);
    for(i = 1; i <= N; ++ i) mnE[i] += mnE[i - 1];

    Mul(mxD, mnA, mnB);
    for(i = N; i >= 1; -- i) mxD[i] += mxD[i + 1];

    Mul(mnD, mxA, mxB);
    for(i = 1; i <= N; ++ i) mnD[i] += mnD[i - 1];

    while(Q --) {
        int x; r1(x);
        long long ans(0);
        ans = 1ll * n * m - 2 * V[x - 1] + mnE[x - 1] - mxE[x] + mxD[x] - mnD[x - 1];
        printf("%lld\n", ans);
    }

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值