@COCI 2016/2017 Round [email protected] Meksikanac

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

在平面直角坐标系中,给定一个左下角为 (0, 0),右上角为 (Xp, Yp) 的矩形,并给定矩形内的 N 个点。

已知坐标系内有一个 K 边形,现将这个 K 边形平移。
问有多少种方案使最后 K 边形落入给定的矩形,定点在整数点上,且不包含 N 个点中的任意一个(与 K 边形的边、顶点重合也算包含)。

input
第一行包含三个整数 Xp, Yp, N (1 <= Xp, Yp <= 500, 0 <= N <= Xp*Yp )。
接下来 N 行每行包含两个整数 X, Y(0 < X < Xp, 0 < Y < Yp ),描述矩阵内的点。
接下来一行包含一个整数 K (3 <= K <= 10000)。
接下来 K 行每行包含两个整数 Xi, Yi(-10^9 <= Xi, Yi <= 10^9),描述了 K 边形的顶点。将这 K 个顶点顺次连接即可得到多边形。

output
输出一个整数,表示将 K 边形平移入矩形,且满足题目所给的限制的方案数。

sample input
5 5 3
1 4
1 3
2 2
3
4 7
6 3
7 6
sample output
3

@[email protected]

P.S:代码比文字也许更具说服力

看起来 K 边形顶点坐标都很大,但其实很容易发现如果要将 K 边形装进矩形,K 边形的大小肯定比矩形小。
所以我们可以将 K 边形的最小横坐标与最小纵坐标对应的点作为 K 边形的参照点,将这个参照点平移至原点,然后看 K 边形是否超出矩形的边界。如果超出直接输出方案数为 0。

考虑怎么求出 K 边形内部的所有点。先想想我们平时判断点是否在多边形内的方法:转角法、射线法。
某小蓝书强烈建议了转角法。然而你发现对于这道题,枚举每一个点然后用转角法判断是 O(m^2*K),其中 m 与 Xp, Yp 是同阶的。

但对于射线法就不一样了。因为所有点都是整数点,假如我们过 (x, y) 作一条垂直向下的射线,则我们完全可以利用 (x, y-1) 作出的射线进行转移,这样就可以不用重复计算,提高效率。
于是我们只需要从 y = 0 开始往右扫描,对于每一个 y 枚举 K 条边,看这 K 条边与当前的扫描线之间的关系,维护出射线法需要的东西。然后再从 x = 0 开始往上扫描,相当于将射线的端点一格一格往上移动。
这样时间复杂度就是 O(m^2 + mK) = O(mK) 的。

射线法在网上有很多很详细的教程,我这里仅提几点要点:
(1)对于点本身在边/顶点上的情况,特判。
(2)对于点引出的射线穿过顶点,考虑射线上的点仅属于射线的左边即可回避这个问题,判断相交时只需要看边的两个端点是否在射线的两边即可。

之后,我们可以枚举 K 边形的一个位置,再依次检查 N 个点是否合法。检查合法只需要看这个点与 K 边形的参照点的相对位置是否在我们之前算出来的所有在 K 边形内的点之中。
但是可以发现这个过程是 O(n^4) 的。

然后可能就是比较套路化的东西:我们将 K 边形内部的点全部记权值为 1,将 N 个点也全部记权值为 1。
决定了 K 边形参照点的位置过后,将对应位置的权值相乘然后相加,可以发现如果存在一个点在 K 边形内,则 1*1 = 1,最后结果就会非 0。
然后发现这是一个卷积的形式。

具体一点:我们记 A[i][j] 表示与 K 边形参照点相对坐标为 (i, j) 上的点是否在多边形内。
再记 B[i][j] 表示矩形中坐标 (i, j) 上是否有 N 个点中的一个。通过下面的式子计算:
\[C[x][y]=\sum_{i}\sum_{j}A[i][j]*B[x+i][y+j]\]

如果 C[x][y] 为 0,说明将参照点平移至 (x, y) 是一种合法方案。
如果想要知道怎么计算这个式子,可以看参考这里(我懒得再写一遍了)。

@accepted [email protected]

#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MOD = 998244353;
const int G = 3;
const int MAXN = 1024;
const int MAXK = 10000;
const int INF = int(1E9);
struct vector{
    int x, y;
    vector(int _x=0, int _y=0):x(_x), y(_y){}
    friend vector operator + (vector a, vector b) {return vector(a.x + b.x, a.y + b.y);}
    friend vector operator - (vector a, vector b) {return vector(a.x - b.x, a.y - b.y);}
}pnt[MAXK + 5];
int A[MAXN + 5][MAXN + 5], B[MAXN + 5][MAXN + 5], C[MAXN + 5][MAXN + 5], D[MAXN + 5][MAXN + 5];
int pow_mod(int b, int p) {
    int ret = 1;
    while( p ) {
        if( p & 1 ) ret = 1LL*ret*b%MOD;
        b = 1LL*b*b%MOD;
        p >>= 1;
    }
    return ret;
}
void ntt(int A[], int len, int type) {
    for(int i=0,j=0;i<len;i++) {
        if( i < j ) swap(A[i], A[j]);
        for(int k=(len>>1);(j^=k)<k;k>>=1);
    }
    for(int s=2;s<=len;s<<=1) {
        int t = (s>>1), u = (type == 1) ? pow_mod(G, (MOD-1)/s) : pow_mod(G, (MOD-1)-(MOD-1)/s);
        for(int i=0;i<len;i+=s) {
            for(int j=0,p=1;j<t;j++,p=1LL*p*u%MOD) {
                int x = A[i+j], y = 1LL*p*A[i+j+t]%MOD;
                A[i+j] = (x + y)%MOD, A[i+j+t] = (x + MOD - y)%MOD;
            }
        }
    }
    if( type == -1 ) {
        int inv = pow_mod(len, MOD-2);
        for(int i=0;i<len;i++)
            A[i] = 1LL*A[i]*inv%MOD;
    }
}
int main() {
    int Xp, Yp, N, K;
    scanf("%d%d%d", &Xp, &Yp, &N);
    for(int i=1;i<=N;i++) {
        int x, y; scanf("%d%d", &x, &y);
        A[x][y] = 1;
    }
    scanf("%d", &K);
    int mnx = INF, mny = INF, mxx = -INF, mxy = -INF;
    for(int i=1;i<=K;i++) {
        scanf("%d%d", &pnt[i].x, &pnt[i].y);
        mnx = min(mnx, pnt[i].x), mny = min(mny, pnt[i].y);
    }
    vector d = vector(mnx, mny);
    for(int i=1;i<=K;i++) {
        pnt[i] = pnt[i] - d;
        mxx = max(mxx, pnt[i].x), mxy = max(mxy, pnt[i].y);
    }
    pnt[K + 1] = pnt[1];
    if( mxx > Xp || mxy > Yp ) {
        puts("0");
        return 0;
    }
    for(int x=0;x<=mxx;x++) {
        for(int i=1;i<=K;i++) {
            vector p1 = pnt[i], p2 = pnt[i + 1];
            if( p1.x == p2.x ) {
                if( p1.x == x ) {
                    for(int y=min(p1.y, p2.y);y<=p1.y||y<=p2.y;y++)
                        B[x][y] = 1;
                }
            }
            else {
                int y = ceil(p1.y + 1.0*(x - p1.x)*(p2.y - p1.y)/(p2.x - p1.x));
                if( (p1.x <= x && x <= p2.x) || (p2.x <= x && x <= p1.x) )
                    if( (p2.x - p1.x)*(y - p1.y) == (x - p1.x)*(p2.y - p1.y) )
                        B[x][y] = 1;
                if( (p1.x <= x && x < p2.x) || (p2.x <= x && x < p1.x) )
                    C[x][y]++;
            }
        }
        int d = 0;
        for(int y=0;y<=mxy;y++) {
            d += C[x][y];
            if( d & 1 ) B[x][y] = 1;
        }
    }
    for(int i=0;i<=Xp;i++)
        for(int j=0;j<=Yp;j++)
            if( j < Yp - j ) swap(A[i][j], A[i][Yp - j]);
    for(int j=0;j<=Yp;j++)
        for(int i=0;i<=Xp;i++)
            if( i < Xp - i ) swap(A[i][j], A[Xp - i][j]);
    int len; for(len= 1; len <= 2*Xp || len <= 2*Yp; len <<= 1);
    for(int i=0;i<len;i++)
        ntt(A[i], len, 1), ntt(B[i], len, 1);
    for(int i=0;i<len;i++)
        for(int j=0;j<len;j++)
            if( i < j ) swap(A[i][j], A[j][i]), swap(B[i][j], B[j][i]);
    for(int i=0;i<len;i++)
        ntt(A[i], len, 1), ntt(B[i], len, 1);
    for(int p=0;p<len;p++)
        for(int q=0;q<len;q++)
            D[q][p] = (D[q][p] + 1LL*A[q][p]*B[q][p]%MOD)%MOD;
    for(int i=0;i<len;i++)
        ntt(D[i], len, -1);
    for(int i=0;i<len;i++)
        for(int j=0;j<len;j++)
            if( i < j ) swap(D[i][j], D[j][i]);
    for(int i=0;i<len;i++)
        ntt(D[i], len, -1);
    for(int i=0;i<=Xp;i++)
        for(int j=0;j<=Yp;j++)
            if( j < Yp - j ) swap(D[i][j], D[i][Yp - j]);
    for(int j=0;j<=Yp;j++)
        for(int i=0;i<=Xp;i++)
            if( i < Xp - i ) swap(D[i][j], D[Xp - i][j]);
    int ans = 0;
    for(int p=0;p<=Xp-mxx;p++)
        for(int q=0;q<=Yp-mxy;q++)
            if( !D[p][q] ) ans++;
    printf("%d\n", ans);
}

@[email protected]

比较套路化的一道题吧。。。思路上相较而言没有什么新奇的地方。。。

不过对于好久没有复习卷积和计算几何的我倒是可以练一练复习一下。

所以我也不知道为什么这样一道题我可以废话这么多。。。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11128482.html

时间: 2024-11-09 00:08:18

@COCI 2016/2017 Round [email protected] Meksikanac的相关文章

@atcoder - CODE FESTIVAL 2017 Final - [email&#160;protected] Tree MST

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定 N 个点,第 i 点有一个点权 Xi,再给定一棵边带权的树,第 i 条 (Ai, Bi) 边权为 Ci. 构建一个完全图,完全图中边 (i, j) 的边权为 dist(i, j) + Xi + Xj,其中 dist(i, j) 是点 i 与点 j 在树上的距离. 求该完全图的最小

[SinGuLaRiTy] COCI 2016~2017 #5

[SinGuLaRiTy-1008] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 测试题目 对于所有的题目:Time Limit:1s  |  Memory:256 MB 第一题:银行账户(account)[parato] [题目描述] 大家都知道28定律吧,据说世界上20%的人拥有80%的财富.现在你对一家银行的账户进行检测,看是否符合28定律,或者有更强的定律.比如说,10%的人拥有85%的财富.更准确的描述是:对N个银行账户进行

@loj - [email&#160;protected] 「清华集训 2017」生成树计数

目录 @[email protected] @[email protected] @正文@ @补充@ @accepted [email protected] @[email protected] @[email protected] 在一个 s 个点的图中,存在 s - n 条边,使图中形成了 n 个连通块,第 i 个连通块中有 \(a_i\) 个点. 现在我们需要再连接 n - 1 条边,使该图变成一棵树.对一种连边方案,设原图中第 i 个连通块连出了 \(d_i\) 条边,那么这棵树 T 的

GDCPC2016题解 by [email&#160;protected] | Asiimov

Problem A. ABCD 题目大意 给出一个四边形四条边AB.CD.AD.BC及两条对角线AC.BD的长度,问这个四边形的顶点能否在一个圆上 算法思路 通过余弦定理考察∠ACB与∠ADB是否相等即可. 时间复杂度: O(1) 代码 /** * Copyright ? 2016 Authors. All rights reserved. * * FileName: A.cpp * Author: Beiyu Li <[email protected]> * Date: 2016-05-09

mysql开启skip-name-resolve 导致[email&#160;protected](localhost)访问引发的ERROR 1045 (28000)错误解决方案

为什么配置skip-name-resolve? 由于mysql -h${ip} 远程访问速度过慢, mysql -h172.16.66.171 -uroot -p123456 根据网友经验(https://www.cnblogs.com/yjf512/p/3803762.html), vi /etc/my.cnf [mysqld] skip-name-resolve 重启mysql,发现远程访问msyql速度上来了,解决问题. 然而引发了新的问题: 但是却发现msyql(mysql -h127.

解决错误 undefined reference to symbol &#39;[email&#160;protected]@CXXABI_1.3.8&#39;

1. 错误信息 在Makefile里,定义CC为"aarch64-linux-gnu-g++ --sysroot=$(MPSOC_ROOTFS)",编译test.cpp,没有错误.如果使用environment-setup-aarch64-xilinx-linux里的设置,在Makefile里不定义CC,编译test.cpp出现下列错误. [email protected]:/proj/hankf/zcu106/v183/egl$ make aarch64-xilinx-linux-g

$*和[email&#160;protected]之间区别代码分析

#!/bin/bash set 'apple pie' pears peaches for i in $*           /*单引号被去掉,循环单个字符输出*/ do echo $i done [[email protected] Ex_14.02-14.31]# sh 14-14-1 apple pie pears peaches -------------------------------------------------------------- #!/bin/bash set

[email&#160;protected]一个高效的配置管理工具--Ansible configure management--翻译(六)

无书面许可请勿转载 高级playbook Finding files with variables All modules can take variables as part of their arguments by dereferencing them with {{ and }} . You can use this to load a particular file based on a variable. For example, you might want to select a

【转载】 ERROR 1045 (28000): Access denied for user [email&#160;protected] (using password: NO)

来自:http://www.jb51.net/LINUXjishu/10981.html 错误描述: Mysql中添加用户之后可能出现登录时提示ERROR 1045 (28000): Access denied for user的错误.删除user.user中值为NULL的,或更新NULL为test 1)delete from user where user is NULL 2)update user set user='test' where user is NULL.意外的情况: 如果上述方