Codeforces 题面传送门 & 洛谷题面传送门

一道鸽了整整一年的题目,上一次提交好像是 2020 年 9 月 13 日来着的(?)

乍一看以为第 2 个提交和第 3 个提交只差了 43min,实际上是 365 天 + 43min = 1314043min

言归正传,进入正题:

首先这个 \(L\) 的数据范围肯定要让我们离散化对吧。我们考虑离散化之后枚举上下边界,也就是,矩形中最上面一个存在棋子的行 \(i\) 以及 矩形中最下面一个存在棋子的行 \(j\),那么如果我们设 \(ux_i\) 表示将 \(x_i\) 从小到大排序并去重后的数组,那么对于一对 \((i,j)\) 而言,在原 \(L\times L\) 的矩形中符合条件的上边界个数就是 \(ux_{i}-ux_{i-1}\),下边界个数就是 \(ux_{j+1}-ux_j\),其中 \(ux_{nx+1}=L+1\),\(nx\) 为 \(x_i\) 中包含的不同的数的个数。

接下来考虑枚举左右边界。一个非常自然的思路是效仿之前枚举上下边界的思路,枚举最左边那个存在棋子的列 \(l\),以及最右边那个存在棋子的列 \(r\),方案数自然就是 \((uy_{l}-uy_{l-1})·(uy_{r+1}-uy_r)\),其中 \(uy\) 的定义同 \(ux\)。这样暴力是四方的。不过有一个显然的性质是符合要求的 \(r\) 是一段后缀,并且这段后缀的左端点随着 \(l\) 的增大而增大,因此可以 two pointers 优化到三方,但还是过不去。

考虑进一步优化,我们记 \(f_l\) 表示当左端点为 \(l\) 时符合条件的右端点 \(r\) 取到最小值时的 \(uy_r\),那么对于一个固定的 \(r\),其贡献就是 \((uy_l-uy_{l-1})·(L+1-f_l)\),总贡献就可以写作

\[\begin{aligned}&\sum\limits_{i=1}^{ny}(uy_i-uy_{i-1})·(L+1-f_i)\\
=&\sum\limits_{i=1}^{ny}(uy_i-uy_{i-1})·(L+1)-\sum\limits_{i=1}^{ny}(uy_i-uy_{i-1})·f_i\\
=&(L+1)·uy_{ny}-\sum\limits_{i=1}^{ny}(uy_i-uy_{i-1})·f_i\end{aligned}
\]

我们只需维护后面那坨东西即可。那么我们可以考虑一个扫描线的思想,即考虑当加入/删除一个元素时对 \(f_l\) 的影响。加入不好处理,因此考虑处理删除一个元素时的贡献。我们假设删除的元素的纵坐标为 \(y\),颜色为 \(c\),那么考虑开一个 set 维护所有颜色的出现位置的列坐标,每次删除我们就在 set 中找到上一个颜色为 \(c\) 的格子的列,假设为 \(pre\),以及下一个颜色为 \(c\) 的格子的列,假设为 \(nxt\),那么显然对于 \(l\in[pre+1,y]\),\(f_l\) 要对 \(nxt\) 取 \(\max\),也就是说我们要支持区间取 \(\max\) 和全局求和两个操作。乍一看要《某科技树》,不过细想其实不需要,因为 \(f\) 数组是单调的,我们可以二分找到最后一个 \(<nxt\) 的位置,然后执行区间赋值即可。

时间复杂度 \(n^2\log n\)。

u1s1 强行上离散化的出题人都是 sb 出题人

const int MAXN=2000;
const int MOD=1e9+7;
int n,k,L,x[MAXN+5],y[MAXN+5],c[MAXN+5];
int kx[MAXN+5],ky[MAXN+5],ux[MAXN+5],uy[MAXN+5],nx,ny;
vector<int> px[MAXN+5],py[MAXN+5];
int pos[MAXN+5],res=0;
struct node{int l,r,val,sm,lz,rit;} s[MAXN*4+5];
multiset<int> occ[MAXN+5];
void pushup(int k){
s[k].val=(s[k<<1].val+s[k<<1|1].val)%MOD;
s[k].rit=s[k<<1|1].rit;
}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r) return s[k].sm=uy[l]-uy[l-1],void();
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
s[k].sm=(s[k<<1].sm+s[k<<1|1].sm)%MOD;
}
void tag(int k,int v){s[k].val=1ll*s[k].sm*v%MOD;s[k].lz=s[k].rit=v;}
void pushdown(int k){if(s[k].lz) tag(k<<1,s[k].lz),tag(k<<1|1,s[k].lz),s[k].lz=0;}
void modify(int k,int l,int r,int v){
if(l>r) return;
if(l<=s[k].l&&s[k].r<=r) return tag(k,v),void();
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) modify(k<<1,l,r,v);
else if(l>mid) modify(k<<1|1,l,r,v);
else modify(k<<1,l,mid,v),modify(k<<1|1,mid+1,r,v);
pushup(k);
}
int walk(int k,int v){//find the leftmost position >v
if(s[k].l==s[k].r) return (s[k].rit>v)?s[k].l:(s[k].l+1);
pushdown(k);
if(s[k<<1].rit>v) return walk(k<<1,v);
else return walk(k<<1|1,v);
}
int query(){return s[1].val;}
void makemax(int l,int r,int v){
// printf("makemax %d %d %d\n",l,r,v);
if(l>r) return;int p=walk(1,v)-1;
chkmin(p,r);modify(1,l,p,v);
}
int main(){
scanf("%d%d%d",&n,&k,&L);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&x[i],&y[i],&c[i]);
++x[i];++y[i];kx[i]=x[i];ky[i]=y[i];
} sort(kx+1,kx+n+1);sort(ky+1,ky+n+1);
for(int i=1;i<=n;i++) if(kx[i]^kx[i-1]) ux[++nx]=kx[i];
for(int i=1;i<=n;i++) if(ky[i]^ky[i-1]) uy[++ny]=ky[i];
for(int i=1;i<=n;i++){
x[i]=lower_bound(ux+1,ux+nx+1,x[i])-ux;
y[i]=lower_bound(uy+1,uy+ny+1,y[i])-uy;
px[x[i]].pb(i);py[y[i]].pb(i);
} ux[nx+1]=uy[ny+1]=L+1;build(1,1,ny);
for(int i=1;i<=k;i++) occ[i].insert(0),occ[i].insert(ny+1);
for(int i=1;i<=nx;i++){
multiset<int> st;
for(int j=1;j<=k;j++) pos[j]=ny+1,st.insert(ny+1);
for(int j=ny;j;j--){
for(int id:py[j]) if(x[id]>=i){
st.erase(st.find(pos[c[id]]));
pos[c[id]]=j;st.insert(pos[c[id]]);
occ[c[id]].insert(j);
} modify(1,j,j,uy[*st.rbegin()]);
// printf("%d%c",uy[*st.rbegin()]," \n"[j==1]);
}
for(int j=nx;j>=i;j--){
res=(res+1ll*(ux[i]-ux[i-1])*(ux[j+1]-ux[j])%MOD
*(1ll*uy[ny]*(L+1)%MOD-query()+MOD))%MOD;
// printf("%d %d %d\n",i,j,s[1].val);
// printf("%d\n",1ll*(ux[i]-ux[i-1])*(ux[j+1]-ux[j])%MOD
// *(1ll*uy[ny]*(L+1)%MOD-s[1].val+MOD)%MOD);
for(int id:px[j]){
occ[c[id]].erase(occ[c[id]].find(y[id]));
int pre=*--occ[c[id]].upper_bound(y[id]);
int nxt=*occ[c[id]].upper_bound(y[id]);
makemax(pre+1,y[id],uy[nxt]);
}
}
} printf("%d\n",res);
return 0;
}

Codeforces 1396D - Rainbow Rectangles(扫描线+线段树)的更多相关文章

  1. HDU 3642 - Get The Treasury - [加强版扫描线+线段树]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3642 Time Limit: 10000/5000 MS (Java/Others) Memory L ...

  2. 【BZOJ3958】[WF2011]Mummy Madness 二分+扫描线+线段树

    [BZOJ3958][WF2011]Mummy Madness Description 在2011年ACM-ICPC World Finals上的一次游览中,你碰到了一个埃及古墓. 不幸的是,你打开了 ...

  3. codeforces Good bye 2016 E 线段树维护dp区间合并

    codeforces Good bye 2016 E 线段树维护dp区间合并 题目大意:给你一个字符串,范围为‘0’~'9',定义一个ugly的串,即串中的子串不能有2016,但是一定要有2017,问 ...

  4. HDU 3265/POJ 3832 Posters(扫描线+线段树)(2009 Asia Ningbo Regional)

    Description Ted has a new house with a huge window. In this big summer, Ted decides to decorate the ...

  5. 【bzoj4491】我也不知道题目名字是什么 离线扫描线+线段树

    题目描述 给定一个序列A[i],每次询问l,r,求[l,r]内最长子串,使得该子串为不上升子串或不下降子串 输入 第一行n,表示A数组有多少元素接下来一行为n个整数A[i]接下来一个整数Q,表示询问数 ...

  6. hdu1542 Atlantis(扫描线+线段树+离散)矩形相交面积

    题目链接:点击打开链接 题目描写叙述:给定一些矩形,求这些矩形的总面积.假设有重叠.仅仅算一次 解题思路:扫描线+线段树+离散(代码从上往下扫描) 代码: #include<cstdio> ...

  7. P3722 [AH2017/HNOI2017]影魔(单调栈+扫描线+线段树)

    题面传送门 首先我们把这两个贡献翻译成人话: 区间 \([l,r]\) 产生 \(p_1\) 的贡献当且仅当 \(a_l,a_r\) 分别为区间 \([l,r]\) 的最大值和次大值. 区间 \([l ...

  8. Codeforces 407E - k-d-sequence(单调栈+扫描线+线段树)

    Codeforces 题面传送门 & 洛谷题面传送门 深感自己线段树学得不扎实-- 首先特判掉 \(d=0\) 的情况,显然这种情况下满足条件的区间 \([l,r]\) 中的数必须相同,双针扫 ...

  9. Codeforces Gym 101480C - Cow Confinement(扫描线+线段树)

    题面传送门 题意: 有一个 \(10^6\times 10^6\) 的地图.其中 \(m\) 个位置上有花,\(f\) 个矩形外围用栅栏围了起来.保证 \(f\) 个矩形两两之间没有公共点. \(q\ ...

随机推荐

  1. Linux 命令后&的作用

    cp $filename /dev/ & & 代表非阻塞方式拷贝文件,如果不加& 则必须等到执行完该指令后才能执行后来的指令.

  2. Scrum Meeting 0503

    零.说明 日期:2021-5-3 任务:简要汇报两日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 两日内已完成的任务 后两日计划完成的任务 qsy PM&前端 完成登录.后端管理 ...

  3. elasticsearch基于RBAC认证和集群之间的TLS通讯

    elasticsearch基于RBAC认证和集群之间的TLS通讯 一.背景 二.需要解决的问题 三.给es增加用户名和密码访问 1.修改config/elasticsearch.yml 2.访问es集 ...

  4. 启动Dubbo项目注册Zookeeper时提示zookeeper not connected异常原理解析

    文/朱季谦 遇到一个很诡异的问题,我在启动多个配置相同zookeeper的Dubbo项目时,其他项目都是正常启动,唯独有一个项目在启动过程中,Dubbo注册zookeeper协议时,竟然出现了这样的异 ...

  5. sql server 如何跟更新拼接的数据(cast用法)

    我们在实际中会做如下图的连接 执行以后这个连接就会报错了,如下图所示   然后我们用cast将数字转换为字符串在连接,如下图所示     这次连接的结果就没问题了,如下图所示     最后如果两个数字 ...

  6. arm开发板上找不到/dev/i2c-*设备

    最近在调试arm与外设iic通讯是,想来个投机取巧,先不写单独的驱动,直接通过iic bus设备是否可以连接到外设,然后发现在板子上找不到"/dev/i2c-n"的设备,标准的系统 ...

  7. Linux Ubuntu stty 使用

    stty(set tty)命令用于显示和修改当前注册的终端的属性. 该命令是一个用来改变并打印终端行设置的常用命令. stty -a #将所有选项设置的当前状态写到标准输出中 old_stty_set ...

  8. 第10课 OpenGL 3D世界

    加载3D世界,并在其中漫游: 在这一课中,你将学会如何加载3D世界,并在3D世界中漫游.这一课使用第一课的代码,当然在课程说明中我只介绍改变了代码. 这一课是由Lionel Brits (βtelge ...

  9. notepad++ 替换回车换行

    以" | "为分隔符,换行 结果如下图:

  10. 安装、卸载 node.js出错 Could not access network location *:\node.js\ 出错

    上周五,WIN10自动更新系统,导致我的node.js 和 Gradle 还有解压的winRAR都不能用!!!可恶 自动更新!!可恶啊!!! 然后我想把node.js重新卸载了再安装,结果 很慌很慌, ...