SHOI2016方

Posted luoyibujue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SHOI2016方相关的知识,希望对你有一定的参考价值。

/*
上帝说 要方
是的 很方
计数问题的容斥思想

 (首先要注意 正方形有斜着的QAQ)
考虑我们要求的合法正方形 ans 根据容斥
ans = 无限制方案书 - 一个点确定的方案数 + 两个点确定的方案数 - 三个点确定的方案数 + 四个点确定的方案数

无限制方案数:
首先假设我们选择了一个n * n的正方形
那么这个正方形就包含了 n - 1种边界在正方形边上的正方形  根据这个来求出总方案数

一个点确定的方案数:(from huanghongxun‘s blog)
考虑每个被删除的点,其对上半,左半,右半,下半部分的影响类似,重复计算的就是正着的正方形的个数,即长宽的较小值。
用(l,r,h)(l,r,h)表示一个区域,删除的点在底边界上,左边有l个坐标,右边有r个坐标。
考虑(6+6)*6的区域。
倾斜0格的有6个,1格的有5个,2格的有4个,……,5格的有1个,6格的有6个,总的是27个。
如果是(6+6)*5的区域,那么就是5,4,3,2,1,5了。
如果是(2+2)*5的区域,那么就是2,2,2,2。
令z=min{l+r,h}z=min{l+r,h}
我们先假设高度要不大于左右侧,那么此时的答案就是z(z+3)/2。
如果大于了左右侧,那么考虑减去多计算的部分,如果左右侧补全到z,那么多出来的部分即n=z?l或z?rn=z?l或z?r,公式即为n(n+1) /2。
两个点确定的方案数:
三个点确定的方案数:
四个点确定的方案数:
这三个可以枚举两个已经确定的点, 然后算出剩下的两个点进行计算
确定三个的 除以3  确定四个的 除以6
愉快地解决
*/
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<set>
#include<iostream>
#define ll long long
#define M 5100
const int mod = 100000007;
using namespace std;
struct P {
    int x,y;
    bool operator < (const P &b) const {
        return x == b.x ? y < b.y: x < b.x;
    }
} note[M],a,b;
int read() {
    int nm = 0, f = 1;
    char c = getchar();
    for(; !isdigit(c); c = getchar()) if(c == -) f= -1;
    for(; isdigit(c); c = getchar()) nm = nm * 10 + c - 0;
    return nm * f;
}
ll ans = 0, n, m, k, cnt3, cnt4;
set<P>st;
ll wk1(int l, int r, int h) {
    int z = min(l + r, h);
    if(z == 0) return 0;
    ll zz = 1ll * z * (z + 3) / 2;
    if(z > l) zz -= 1ll * (z - l) * (z - l + 1) / 2;
    if(z > r) zz -= 1ll * (z - r) * (z - r + 1) / 2;
    return zz;
}

void solve1() {
    for(int i = 1; i <= k; i++) {
        int x = note[i].x, y = note[i].y, l = x, r = n - x, u = y, d = m - y;
        ans -= (wk1(l,r,u) + wk1(l,r,d) + wk1(u,d,l) + wk1(u,d,r) - min(l, u) - min(l, d) - min(r, u) - min(r, d));
        ans %= mod;
    }
}

void wk2(P a, P b) {
    if(a.x < 0 || a.x > n || b.x < 0 || b.x > n || a.y < 0 || b.y < 0 || a.y > m || b.y > m) return;
    ans++;
    int op = st.count(a) + st.count(b);
    if(op == 1) cnt3++;
    if(op == 2) cnt3 += 2, cnt4++;
}

void solve234() {
    for(int i = 1; i <= k; i++) {
        a = note[i];
        for(int j = i + 1; j <= k; j++) {
            b = note[j];
            int dx = a.x - b.x, dy = a.y - b.y, xx, yy;
            /*两点相邻的*/
            wk2((P){a.x + dy, a.y - dx}, (P){b.x + dy, b.y - dx});
            wk2((P){a.x - dy, a.y + dx}, (P){b.x - dy, b.y + dx});
            if((abs(dx) + abs(dy)) & 1) continue;
                    /*对角线的*/
            xx = dx - dy >> 1, yy = dx + dy >> 1;
            wk2((P){a.x - xx, a.y - yy}, (P){b.x + xx, b.y + yy});
        }
    }
}

int main() {
    n = read(), m = read(), k = read();
    for(int i = 1; i <= k; i++) {
        note[i].x = read(), note[i].y = read();
        st.insert(note[i]);
    }
    for(int i = 1; i <= min(n, m); i++) ans += 1ll * i * (n - i + 1) * (m - i + 1), ans %= mod;
    solve1();
    solve234();
    ans -= cnt3 / 3 - cnt4 / 6;
    cout << ((ans % mod) + mod) % mod;
    return 0;
}

 

以上是关于SHOI2016方的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4596:[Shoi2016]黑暗前的幻想乡

p4336 [SHOI2016]黑暗前的幻想乡

BZOJ 4596: [Shoi2016]黑暗前的幻想乡

BZOJ4597[Shoi2016]随机序列 线段树

SHOI2016 随机序列

P4336 [SHOI2016]黑暗前的幻想乡