2018.8.5比赛T1(前缀和)

描述
蔡老师买了 m 门大炮,放置在一个 n∗n 的网格图上,行和列的编号都为 1…n, 第 i 门大炮架第 x 行第 y 列的格子上。同一个格子上可能架了多门大炮。

然而蔡老师买的大炮很奇怪,他能攻击左上,右上,左下,右下四个方向的格子,即 (x,y) 上的大炮能攻击到 (u,v),当且仅当 x+y=u+v 或者 x−y=u−v。

我们称一个格子被蔡老师占领了,当且仅当这个格子上有蔡老师的大炮,或者说蔡老师有任意一门大炮可以攻击到这个格子。

现在蔡老师想知道,还有多少个格子没有被他占领

输入格式
第一行两个正整数 n,m
接下来 m 行,每行两个正整数 (x,y)描述蔡老师的一门大炮的位置

输出格式
输出有多少个格子没有被占领

样例1
样例输入
3 1
1 1
样例输出
6
这里写图片描述


正解挺显然的,不讲部分分了
因为只有两个斜的方向
我们考虑以两个斜向作为基准方向
先各自考虑经过的格子
考虑交叉部分,假设正对角线已经放好了(肯定不会交叉)
因为只有这两个基准方向,所以负对角线只会和原来放好的正对角线相交,且互相不影响
把对角线抽象到区间上。
我们发现负对角线能交的必定是一段区间
这里我们又发现可能出现交在割点上的情况
其实我们写出二元一次方程就可以发现一个奇偶性的关系
不过你仅仅是随便画几个也能发现
好了那么问题就简单了
只要对奇偶位置分开做前缀和就好了
考场上zz的我以为带修改写了个线段树

线段树版本:(懒得改了)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define ls root * 2
#define rs root * 2 + 1
int tr[801000];
int tr2[801000];
int n , m;
bool a[201000] , b[201000];//a为正对角线,b为负对角线
ll ans;
int read()
{
    int sum = 0;char c = getchar();bool flag = true;
    while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}
    while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();
    if(flag)  return sum;
     else return -sum;
}
int query(int root,int l,int r,int x,int y)
{
    if(l > y || r < x) return 0;
    if(x <= l && r <= y) return tr[root];
    int mid = (l + r)>>1;
    return query(ls,l,mid,x,y) + query(rs,mid+1,r,x,y);
}
int query2(int root,int l,int r,int x,int y)
{
    if(l > y || r < x) return 0;
    if(x <= l && r <= y) return tr2[root];
    int mid = (l + r)>>1;
    return query2(ls,l,mid,x,y) + query2(rs,mid+1,r,x,y);
}
void change(int root,int l,int r,int x)
{
    if(l == r)
    {
        tr[root]++;
        return;
    }
    int mid = (l + r)>>1;
    if(x <= mid) change(ls,l,mid,x);
    else change(rs,mid+1,r,x);
    tr[root] = tr[ls] + tr[rs];
    return;
}
void change2(int root,int l,int r,int x)
{
    if(l == r)
    {
        tr2[root]++;
        return;
    }
    int mid = (l + r)>>1;
    if(x <= mid) change2(ls,l,mid,x);
    else change2(rs,mid+1,r,x);
    tr2[root] = tr2[ls] + tr2[rs];
    return;
}
void init()
{
    n = read();m = read();
    rep(i,1,m)
    {
        int x = read() , y = read();
        a[x-y+n] = true;
        b[x+y-2] = true;
    }
    rep(i,1,2*n-1) 
        if(a[i]) ans += i <= n ? i : 2 * n - i;
    rep(i,0,2*n-2)
        if(b[i]) ans += i <= n-1 ? i + 1 : 2*n-1-i;
    rep(i,1,2*n-1)
        if(a[i])
            if(i % 2 == 1) change(1,1,2*n-1,i);
            else change2(1,1,2*n-1,i);
    rep(i,0,2*n-2)
    {
        if(!b[i]) continue;
        int now = i;
        if(i > n-1) now = 2*n-2-now;
        if((n - now) % 2 == 1) ans -= query(1,1,2*n-1,n-now,n+now);
        else ans -= query2(1,1,2*n-1,n-now,n+now);
    }
    printf("%lld\n",1ll*n*n-ans);
    return;
}
int main()
{
    init();
    return 0;
} 

索性不是太复杂

猜你喜欢

转载自blog.csdn.net/a1035719430/article/details/81435125
t1
今日推荐