洛谷p3801:红色的幻想乡

初见完全没有思路.....感觉像是线段树 但二维感觉完全不可做嘛

于是只能去看了看题解 然而还是疯狂爆零+WA..

和yycc神犇调了两三个小时才调出来...

——————以下个人理解

考虑到每次的修改都是对整行和整列进行操作

可以把每行缩成一个点 这样修改就相当于对这个点进行单点修改

同理也把每列缩成一个点

那么对于每一次修改操作 我们只需要将这个点的横坐标与纵坐标进行修改即可

也就是维护两棵线段树,分别表示行和列

显然可以看出对于图里的每一个点,只有有红雾和没红雾两种状态,并且又说两次红雾会抵消

于是每一次修改就相当于做一次取反操作 还需要支持的另一个操作就是朴素的区间求和

但这显然不是正解 因为每一次操作时实际对于蕾咪所在的那个点是完全没有影响的 而在我们的修改时没有考虑到这一点

似乎没有什么好办法?......好像标记的话会退化回O(Nlogn)....

当然是选择容斥它辣.....但是蒟蒻博主也不会...我太弱啦!

又请教了一下yycc神犇

画图可知 a条横着的直线与b条竖着的直线的交点数为a*b

而每一个交点我们在横竖修改的时候都分别对他对多标记了一次

所以只需在结果上减去一个ansx*ansy*2就是答案了

码农题

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<limits.h>
#include<ctime>
#define N 1000001
typedef long long ll;
const int inf=0x3fffffff;
const int maxn=2017;
using namespace std;
inline ll read()
{
    ll f=1,x=0;char ch=getchar();
    while(ch>‘9‘|ch<‘0‘)
    {
        if(ch==‘-‘)
        f=-1;
        ch=getchar();
    }
    while(ch<=‘9‘&&ch>=‘0‘)
    {
        x=(x<<3)+(x<<1)+ch-‘0‘;
        ch=getchar();
    }
    return f*x;
}
struct tsdl{
    ll w;
}xtr[N],ytr[N];
void updatex(ll l,ll r,ll pos,ll x)
{
    if(l==r)
    {
        xtr[pos].w^=1;
        return;
    }
    ll mid=l+r>>1;
    if(mid>=x)updatex(l,mid,pos<<1,x);
    else updatex(mid+1,r,pos<<1|1,x);
    xtr[pos].w=xtr[pos<<1].w+xtr[pos<<1|1].w;
}
void updatey(ll l,ll r,ll pos,ll x)
{
    if(l==r)
    {
        ytr[pos].w^=1;
        return;
    }
    ll mid=l+r>>1;
    if(mid>=x)updatey(l,mid,pos<<1,x);
    else updatey(mid+1,r,pos<<1|1,x);
    ytr[pos].w=ytr[pos<<1].w+ytr[pos<<1|1].w;
}
ll queryx(ll l,ll r,ll a,ll b,ll pos)
{
    if(l>=a&&r<=b)
    {
        return xtr[pos].w;
    }
    ll ans=0;
    ll mid=l+r>>1;
    if(mid>=a)ans+=queryx(l,mid,a,b,pos<<1);
    if(mid<b)ans+=queryx(mid+1,r,a,b,pos<<1|1);
    return ans;
}
ll queryy(ll l,ll r,ll a,ll b,ll pos)
{
    if(l>=a&&r<=b)
    {
        return ytr[pos].w;
    }
    ll ans=0;
    ll mid=l+r>>1;
    if(mid>=a)ans+=queryy(l,mid,a,b,pos<<1);
    if(mid<b)ans+=queryy(mid+1,r,a,b,pos<<1|1);
    return ans;
}
int main()
{
    ll n=read(),m=read(),k=read();
    while(k--)
    {
        ll opt=read();
        switch(opt)
        {
            case 1:
            {
                ll x=read(),y=read();
                updatex(1,n,1,x);
                updatey(1,m,1,y);
                break;
            }
            case 2:
            {
                ll ans=0;
                ll xa=read(),ya=read(),xb=read(),yb=read();
                ll ansx=queryx(1,n,xa,xb,1);
                ll ansy=queryy(1,m,ya,yb,1);
                cout<<ansy*(xb-xa+1)+ansx*(yb-ya+1)-ansx*ansy*2<<endl;
            }
        }
    }
}
时间: 2024-10-14 07:14:23

洛谷p3801:红色的幻想乡的相关文章

[luogu P3801] 红色的幻想乡 [线段树][树状数组]

题目背景 蕾米莉亚的红雾异变失败后,很不甘心. 题目描述 经过上次失败后,蕾米莉亚决定再次发动红雾异变,但为了防止被灵梦退治,她决定将红雾以奇怪的阵势释放. 我们将幻想乡看做是一个n*m的方格地区,一开始没有任何一个地区被红雾遮盖.蕾米莉亚每次站在某一个地区上,向东南西北四个方向各发出一条无限长的红雾,可以影响到整行/整列,但不会影响到她所站的那个地区.如果两阵红雾碰撞,则会因为密度过大而沉降消失.灵梦察觉到了这次异变,决定去解决它.但在解决之前,灵梦想要了解一片范围红雾的密度.可以简述为两种操

P3801 红色的幻想乡

基本的线段树和容斥原理 我好菜啊! 题目中其实有一点拐了弯:她把两团雾气中和沉降和在原地放置两种情况分开了. 但你只要画个图就能发现:在都有标记的行和列上,一旦有交叉,这个点就是没雾气的. 在一个点放置雾气,相当于在一行和一列都放了雾气. 一个区域内的雾气,相当于所有有标记的行和列占的总面积再减掉那些沉降掉的. 不难发现:当在行中放了\(x\)个雾气,在列中放了\(y\)个雾气时,被中和掉的就有\(xy\)个. 其实也不难算的,手膜一下就知道了. 代码: #include<cstdio> co

AC日记——红色的幻想乡 洛谷 P3801

红色的幻想乡 思路: 线段树+容斥原理: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 100005 #define maxm maxn<<2 #define ll long long class TreeType { private: int L[maxm],R[maxm],mid[maxm],dis[maxm]; public: void build(int now,int l,int r) { L

红色的幻想乡

题目背景 蕾米莉亚的红雾异变失败后,很不甘心. 题目描述 经过上次失败后,蕾米莉亚决定再次发动红雾异变,但为了防止被灵梦退治,她决定将红雾以奇怪的阵势释放. 我们将幻想乡看做是一个n*m的方格地区,一开始没有任何一个地区被红雾遮盖.蕾米莉亚每次站在某一个地区上,向东南西北四个方向各发出一条无限长的红雾,可以影响到整行/整列,但不会影响到她所站的那个地区.如果两阵红雾碰撞,则会因为密度过大而沉降消失.灵梦察觉到了这次异变,决定去解决它.但在解决之前,灵梦想要了解一片范围红雾的密度.可以简述为两种操

LG3801 红色的幻想乡 线段树+容斥原理

问题描述 经过上次失败后,蕾米莉亚决定再次发动红雾异变,但为了防止被灵梦退治,她决定将红雾以奇怪的阵势释放. 我们将幻想乡看做是一个 \(n \times m\) 的方格地区,一开始没有任何一个地区被红雾遮盖.蕾米莉亚每次站在某一个地区上,向东南西北四个方向各发出一条无限长的红雾,可以影响到整行/整列,但不会影响到她所站的那个地区.如果两阵红雾碰撞,则会因为密度过大而沉降消失.灵梦察觉到了这次异变,决定去解决它.但在解决之前,灵梦想要了解一片范围红雾的密度.可以简述为两种操作: 1 x y 蕾米

洛谷 P3224 [HNOI2012]永无乡

(splay_tree_tag没有比rb_tree_tag快) 1 #include<cstdio> 2 #include<algorithm> 3 #include<ext/pb_ds/assoc_container.hpp> 4 #include<ext/pb_ds/tree_policy.hpp> 5 using namespace std; 6 using namespace __gnu_pbds; 7 typedef std::pair<in

luogu3801 红色的幻想乡

题目大意 给一个初始值都是0的0-1矩阵,两个操作:1.选择一个点,将其所在排和列(不包括该点)的数字取反.2.求一个子矩形内的数字和.n,m,q<=100000. 错误思路 为何不能用二维线段树 n,m<=100000,每个x线段树都维护一个有400000个节点的Y线段树,而X节点也要400000个,空间受不了. 如果我们要更新一排,X线段树没有达到"排除一半"的功能,必须遍历到所有X.Y节点,时间受不了. 假命题:将所在排列其它数字取反,等价于把选择的点的数字取反 与后

洛谷P1726 上白泽慧音

P1726 上白泽慧音 124通过 343提交 题目提供者yeszy 标签图论 难度提高+/省选- 提交该题 讨论 题解 记录 最新讨论 给两组数据吧! 题目描述 在幻想乡,上白泽慧音是以知识渊博闻名的老师.春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄.因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点.人间之里由N个村庄(编号为1..N)和M条道路组成,道路分为两种一种为单向通行的,一种为双向通行的,分别用1和2来标记.如果存在由村庄A到达村庄B的通

【BZOJ3924】幻想乡战略游戏(动态点分治)

[BZOJ3924]幻想乡战略游戏(动态点分治) 题面 权限题...(穷死我了) 洛谷 题解 考虑不修改 发现一个贪心的做法 假设当前放在当前位置 如果它有一个子树的兵的总数大于总数的一半 那么,放到那个子树的根节点上一定最优 那么,现在是动态修改 考虑动态点分治 在每个点上维护子树的兵的总数 子树到上一层父亲节点 向上走产生的贡献的总和 以及接收到子节点的贡献的总和 那么,就可以计算当前点产生的贡献 于是,从分治树根开始向下贪心即可 #include<iostream> #include&l