题目链接:

IOI2018seat

题目大意:给出一个$H*W$的矩阵,将$0 \sim W*H-1$分别填入矩阵的格子里(每个格子里一个数),定义一个子矩阵是美妙的当且仅当这个子矩阵包含且仅包含$0 \sim i$($i$为$0 \sim W*H-1$的任意数)这些数,每次调换两个数的位置,求有多少个美妙的子矩阵。

这题思路真的很神。

原题编号从0开始,很不舒服,我们按从1开始的讲。

发现只需要判断[1,i]这些数是否组成了一个矩阵。

那么我们能不能用线段树,第i个叶子节点存前i个数的信息来判断前i个数能否组成矩阵呢?

有的人可能会想到第i个叶子节点维护前i个数中最左上的点和最右下的点,判断时直接取这两个点形成矩形的面积看是否等于i。

这个判断是可行了,但修改呢?你会发现交换两个点的位置会改变好多点的信息,甚至影响的信息达到O(n)级别。

这时真正的神仙操作来了。

在判断前i个点是否成立时我们将前i个点染成黑色,其他点为白色。

我们维护两个信息:

1、有多少黑点的左边和上边都是白点或边界

2、有多少白点的四联通块中包含大于等于2个黑点

可以看出,如果前i个点形成矩形那么第一个信息值为1,第二个信息值为0。同理也只有这种情况才是矩形。

为什么呢?

如果黑点都连在一起形成一个图形,那么第二个信息为0保证他是一个凸多边形且多边形的边与整个图是平行的,而第一个信息为1则保证他有四个顶点。

如果还是不太明白可以手画一下。

不管所有黑点组成什么图形都至少有一个左上顶点,因此第一个信息的值一定是正数。

我们维护两个信息不方便,不妨维护他们两个的和,那么就只有和为1时是成立的。

在线段树上每个叶子节点维护前i个点染黑后两个信息的和,代表区间的那些父节点则维护区间最小值及最小值个数即可。

那么怎么修改?

对于第i个点我们求出它作为白点有贡献的开始时刻l(即它的四联通块中编号第二小的)和作为黑点有贡献的结束时刻r(即它左边和上边两个点中编号最小的)。

那么这个点作为白点时会对[l,i-1]这段时刻有贡献,而作为黑点是会对[i,r-1]这段时刻有贡献。

交换两个点会影响这两个点的四联通块最多10个点的l和r,先减去原先每个点的贡献,交换位置后再对每个点有贡献的时间段区间修改即可,注意这些修改的点要去重。

#include"seats.h"
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define mp(x,y) (b[(x-1)*m+y])
#define sx(i) (a[i].x)
#define sy(i) (a[i].y)
#define fx(x,i) (x+dx[i])
#define fy(x,i) (x+dy[i])
#define check(x,y) (x>=1&&x<=n&&y>=1&&y<=m)
using namespace std;
int dx[4]={0,-1,0,1};
int dy[4]={-1,0,1,0};
int b[1000010];
int mn[4000010];
int sum[4000010];
int t[4000010];
int v[1000010];
int n,m,q;
int x,y;
int st[15];
int top;
struct miku
{
int x;
int y;
}a[1000010];
inline void pushup(int rt)
{
mn[rt]=min(mn[rt<<1],mn[rt<<1|1]);
sum[rt]=0;
if(mn[rt<<1]==mn[rt])
{
sum[rt]+=sum[rt<<1];
}
if(mn[rt<<1|1]==mn[rt])
{
sum[rt]+=sum[rt<<1|1];
}
}
inline void pushdown(int rt)
{
if(t[rt])
{
t[rt<<1]+=t[rt];
t[rt<<1|1]+=t[rt];
mn[rt<<1]+=t[rt];
mn[rt<<1|1]+=t[rt];
t[rt]=0;
}
}
inline void build(int rt,int l,int r)
{
if(l==r)
{
mn[rt]=v[l];
sum[rt]=1;
return ;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
inline void change(int rt,int l,int r,int L,int R,int k)
{
if(L<=l&&r<=R)
{
mn[rt]+=k;
t[rt]+=k;
return ;
}
pushdown(rt);
int mid=(l+r)>>1;
if(L<=mid)
{
change(rt<<1,l,mid,L,R,k);
}
if(R>mid)
{
change(rt<<1|1,mid+1,r,L,R,k);
}
pushup(rt);
}
inline int begin_white(int x)
{
int m1=n*m+1;
int m2=n*m+1;
for(int i=0;i<4;i++)
{
if(check(fx(sx(x),i),fy(sy(x),i)))
{
int now=mp(fx(sx(x),i),fy(sy(x),i));
if(now<m1)
{
m2=m1;
m1=now;
}
else if(now<m2)
{
m2=now;
}
}
}
return m2;
}
inline int end_black(int x)
{
int m1=n*m+1;
for(int i=0;i<2;i++)
{
if(check(fx(sx(x),i),fy(sy(x),i)))
{
m1=min(m1,mp(fx(sx(x),i),fy(sy(x),i)));
}
}
return m1;
}
int swap_seats(int x,int y)
{
x++;
y++;
if(x>y)
{
swap(x,y);
}
top=0;
st[++top]=x;
st[++top]=y;
for(int i=0;i<4;i++)
{
if(check(fx(sx(x),i),fy(sy(x),i)))
{
st[++top]=mp(fx(sx(x),i),fy(sy(x),i));
}
}
for(int i=0;i<4;i++)
{
if(check(fx(sx(y),i),fy(sy(y),i)))
{
st[++top]=mp(fx(sx(y),i),fy(sy(y),i));
}
}
sort(st+1,st+1+top);
for(int i=1;i<=top;i++)
{
if(st[i]!=st[i-1])
{
int l=begin_white(st[i]);
int r=end_black(st[i]);
if(l<st[i])
{
change(1,1,n*m,max(l,x),min(st[i],y)-1,-1);
}
if(r>st[i])
{
change(1,1,n*m,max(st[i],x),min(r,y)-1,-1);
}
}
}
swap(mp(sx(x),sy(x)),mp(sx(y),sy(y)));
swap(a[x],a[y]);
for(int i=1;i<=top;i++)
{
if(st[i]!=st[i-1])
{
int l=begin_white(st[i]);
int r=end_black(st[i]);
if(l<st[i])
{
change(1,1,n*m,max(l,x),min(st[i],y)-1,1);
}
if(r>st[i])
{
change(1,1,n*m,max(st[i],x),min(r,y)-1,1);
}
}
}
return sum[1];
}
void give_initial_chart(int H, int W,vector<int> R,vector<int> C)
{
n=H,m=W;
for(int i=1;i<=n*m;i++)
{
x=R[i-1];
y=C[i-1];
x++;
y++;
a[i].x=x;
a[i].y=y;
b[(x-1)*m+y]=i;
}
for(int i=1;i<=n*m;i++)
{
v[i]=v[i-1];
int l=begin_white(i);
int r=end_black(i);
if(l<i)
{
v[i]--;
}
if(r>i)
{
v[i]++;
}
for(int j=0;j<4;j++)
{
if(check(fx(sx(i),j),fy(sy(i),j)))
{
int now=mp(fx(sx(i),j),fy(sy(i),j));
if(now<i&&end_black(now)==i)
{
v[i]--;
}
else if(now>i&&begin_white(now)==i)
{
v[i]++;
}
}
}
}
build(1,1,n*m);
}

[IOI2018]排座位——线段树的更多相关文章

  1. LOJ.2864.[IOI2018]排座位(线段树)

    LOJ 洛谷 先令编号从\(1\)开始.我们要求\([1,i]\)这些数字能否构成一个矩形. 考虑能否用线段树维护,让每个叶子节点\(i\)表示前\(i\)个数能否构成矩形. 一种方法是维护前\(i\ ...

  2. [IOI2018]会议——分治+线段树

    题目链接: [IOI2018]meetings 题目大意:有$n$座山峰,每座山峰有一个高度,有$q$次询问,每次需要确定一个开会山峰使$[l,r]$所有山峰上的人都前往开会山峰,一个山峰的人去开会的 ...

  3. [IOI2018]机械娃娃——线段树+构造

    题目链接: IOI2018doll 题目大意:有一个起点和$m$个触发器,给出一个长度为$n$的序列$a$,要求从起点出发按$a$的顺序经过触发器并回到起点(一个触发器可能被经过多次也可能不被经过), ...

  4. [IOI2018] seats 排座位

    [IOI2018] seats 排座位 IOI2018题解 压缩状态思想很不错的 每次把原来的贡献减掉,新来的再加上 最多涉及10个点 注意: 1.去重 2.下标从0开始 3.线段树初始的最小值个数都 ...

  5. 洛谷 P3071 [USACO13JAN]座位Seating(线段树)

    P3071 [USACO13JAN]座位Seating 题目链接 思路: 一开始把题给读错了浪费了好多时间呜呜呜. 因为第二个撤离操作是区间修改,所以我们可以想到用线段树来做.对于第一个操作,我们只需 ...

  6. string [线段树优化桶排]

    题意大概是给你一个字符串,1e5次修改,每次给一个区间升序排列或降序排列,最后输出这个字符串; 其实是个挺裸的线段树优化题;但是我没有意识去结合桶排,扑该..... 首先 1.40分算法 O(NMlo ...

  7. [IOI2018]狼人——kruskal重构树+可持久化线段树

    题目链接: IOI2018werewolf 题目大意:给出一张$n$个点$m$条边的无向图,点和边可重复经过,一个狼人初始为人形,有$q$次询问,每次询问要求人形态只能处于编号不小于$L$的点,狼形态 ...

  8. HDU 1166 排兵布阵(线段树单点更新)

    题意: 给定n个兵营的士兵初始值, 然后有最多40000个操作: 操作一共有两种, 一个是查询给定[a,b]区间兵营的士兵总和. 另一个是增加/减少指定兵营的士兵数目. 输出每次查询的值. 分析: 线 ...

  9. 【42%】【hdu1166】排兵布阵(树状数组解法&&线段树解法)

    Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任 ...

随机推荐

  1. ubuntu开机自动运行用Qt写的程序

    这里介绍一种在ubuntu系统开机自动运行使用Qt编写的程序的方法.首先要注意要自动运行Qt编的程序,不需要先打开Qt,而是直接运行编译好的与工程名同名的可执行文件即可,比如我要运行的工程为QRDec ...

  2. C语言程序设计II—第八周教学

    第八周教学总结(15/4-21/4) 教学内容 本周的教学内容为: 8.4 电码加密 知识点:指针与字符串,重难点:字符指针与字符串的关联和区别: 8.5 任意个整数求和 知识点:动态内存分配的概念和 ...

  3. Maven学习笔记-04-Eclipse下maven项目在Tomcat7和Jetty6中部署调试

    现在最新的Eclipse Luna Release 已经内置了Maven插件,这让我们的工作简洁了不少,只要把项目直接导入就可以,不用考虑插件什么的问题,但是导入之后的项目既可以部署在Tomcat也可 ...

  4. Luogu P2059 [JLOI2013]卡牌游戏

    一道比较简单的概率DP 首先看到这种题目和数据范围,就要毫不犹豫地列DP方程: 我们令\(f_{i,j}\)表示还剩下i个人时编号为j的人的胜率,那么首先我们可以知道边界条件\(f_{1,1}=1\) ...

  5. 一次线上redis实例cpu占用率过高问题优化(转)

    前情提要: 最近接了大数据项目的postgresql运维,刚接过来他们的报表系统就出现高峰期访问不了的问题,报表涉及实时数据和离线数据,离线读pg,实时读redis.然后自然而然就把redis也挪到我 ...

  6. Flutter - TabBar导航栏切换后,状态丢失

    上一篇讲到了 Flutter - BottomNavigationBar底部导航栏切换后,状态丢失 里面提到了TabBar,这儿专门再写一下吧,具体怎么操作,来不让TabBar的状态丢失.毕竟大家99 ...

  7. Linux下的计算命令和求和、求平均值、求最值命令梳理

    在Linux系统下,经常会有一些计算需求,那么下面就简单梳理下几个常用到的计算命令 (1)bc命令bc命令是一种支持任意精度的交互执行的计算器语言.bash内置了对整数四则运算的支持,但是并不支持浮点 ...

  8. 个人博客作业_week14

    M1/M2阶段总结 我在M1阶段负责后端代码的开发,以及协助PM,在M2阶段负责PM,在为期将近一学期的团队软件开发过程中,我深刻体会到了团队协作的重要性,以及合理分配任务的重要性,没有一个好的时间规 ...

  9. BugPhobia开发篇章:Beta阶段第VIII次Scrum Meeting

    0x01 :Scrum Meeting基本摘要 Beta阶段第八次Scrum Meeting 敏捷开发起始时间 2015/12/22 00:00 A.M. 敏捷开发终止时间 2015/12/22 23 ...

  10. Beta版测试报告

    Beta版测试报告 测试中发现的Bug: Version 2.0 Bug List 1. 在动态监测界面,若随便点击“开始”.“关闭”.“结束”.红叉,会出现不定式崩溃现象. 2. 处理空数据时可能会 ...