Codeforces 453E - Little Pony and Lord Tirek(二维线段树+ODT)
一道难度 *3100 的 DS,而且被我自己搞出来了!
不过我终究还是技不如人,因为这是一个 \(n\log^2n\) + 大常数的辣鸡做法,几乎是卡着时空限制过去的……
首先注意到对于每个小马,在任意时刻它的法力值只有三种可能:
- \(s_i+kr_i(k\in\mathbb{Z})\)
- \(kr_i(k\in\mathbb{Z})\)
- \(m_i\)
我们考虑对这三种情况一一分析,对于第一种情况这里的 \(k\) 只可能等于我们待查询的 \(t\),并且这种情况可能发生当且仅当 \(i\) 没有被清空过,并且 \(s_i+kr_i\le m_i\),第二个条件好办,我们如果设 \(a_i=\lfloor\dfrac{m_i-s_i}{r_i}\rfloor\)(如果 \(r_i=0\) 那么 \(a_i=\infty\)),那么 \(i\) 符合第二个条件当且仅当 \(a_i\ge t\),第一个条件虽然看起来棘手,但是我们可以维护一个 set
表示没有被清空过的集合,每次进行查询操作就将这个 set
中属于 \([l,r]\) 的元素全部删除,不难发现每个数最多被删除一次,因此复杂度是 \(n\log n\) 的。接下来问题就转化为如何维护这个值,显然它可以转化为 \(\sum\limits_{i=l}^r[a_i\ge t]s_i+t\sum\limits_{i=l}^r[a_i\ge t]r_i\),两部分结构类似,因此维护方法也类似,注意到这里涉及两维,\(i\in[l,r]\) 和 \(a_i\ge t\),那么该操作相当于查询矩形 \((l,a_i),(r,\infty)\) 中权值的和,这个离散化+建立二维线段树就可以搞定了,具体来说先将所有 \((i,a_i)\) 看作一个点并更新其在二维线段树上的权值,每次我们将一个数从 set
中删除就将对应的 \((i,a_i)\) 的贡献撤销掉,正确性显然。
搞清楚这个情况后,我们也可以顺带着把 \(i\) 没有被清空过的贡献都搞清楚,若 \(i\) 没有被清空过,可以分两种情况,\(a_i\ge t\) 那么贡献为 \(s_i+tr_i\),这个刚刚已经探究过了,\(a_i<t\) 那么贡献为 \(m_i\),不难发现这个也可以写成矩形和的形式,同样二维线段树维护即可,这里就不再赘述了。
\(i\) 没有被清空过的情况搞清楚了,接下来考虑 \(i\) 被清空过的情况,显然对于一个被清空过的 \(i\) 它都有一个上次被清空的时间 \(T_i\),在这 \(t-T_i\) 分钟内它的法力值最多可以上涨 \(r_i(t-T_i)\),那么它的贡献就是 \(\min(m_i,r_i(t-T_i))\),还是按照之前的套路,记 \(b_i=\lfloor\dfrac{m_i}{r_i}\rfloor\),还是分两种情况,若 \(b_i\ge t-T_i\),那么贡献为 \(r_i(t-T_i)\),否则贡献为 \(m_i\),这样还是不好做,因为这里的 \(T_i\) 都是互不相同的,无法直接维护。不过不难发现一个性质,那就是任意时刻 \(T_i\) 都是成段分布的,也就是说相同的 \(T_i\) 都是一段一段的,我们记三元组 \((l,r,t)\) 表示 \(\forall i\in[l,r],T_i=t\),我们考虑开一个 set
维护所有三元组并对每个三元组计算贡献——这个就可以按照上面的套路二维线段树了,相信如果理解了前面的部分那这边应该的相当容易的。我们还可以发现所有计算过贡献的三元组 \((l',r',t')\) 必定满足 \(l\le l'\le r'\le r\),这样的三元组在查询过后都会从 set
中删除,标准的 ODT 模型,按照 P4690 [Ynoi2016] 镜中的昆虫 的套路 维护即可。
时间复杂度 \(n\log^2n\),由于要维护五个二维线段树,常数巨大,但还好卡过去了(((
最后说一句,wtm 不是 sb,又拿未去重的数组离散化,话说恰好一年前的今天也犯过这个错误来着的?
const int MAXN=1e5;
const int MAXP=MAXN*20;
const int INF=0x3f3f3f3f;
int n,s[MAXN+5],r[MAXN+5],mx[MAXN+5],lim1[MAXN+5],lim2[MAXN+5];
int key1[MAXN+5],key2[MAXN+5],uni1[MAXN+5],uni2[MAXN+5],num1=0,num2=0;
struct seg2d{
struct node{int ch[2];ll sum;} s[MAXP+5];
int rt[MAXN+5],ncnt;
seg2d(){ncnt=0;memset(rt,0,sizeof(rt));}
void add_in(int &k,int l,int r,int x,int v){
if(!k) k=++ncnt;s[k].sum+=v;
if(l==r) return;int mid=l+r>>1;
if(x<=mid) add_in(s[k].ch[0],l,mid,x,v);
else add_in(s[k].ch[1],mid+1,r,x,v);
}
ll query_in(int k,int l,int r,int ql,int qr){
if(ql>qr||!k) return 0;int mid=l+r>>1;
if(ql<=l&&r<=qr) return s[k].sum;
if(qr<=mid) return query_in(s[k].ch[0],l,mid,ql,qr);
else if(ql>mid) return query_in(s[k].ch[1],mid+1,r,ql,qr);
else return query_in(s[k].ch[0],l,mid,ql,mid)+query_in(s[k].ch[1],mid+1,r,mid+1,qr);
}
void add_out(int x,int p,int v){
for(int i=x;i<=n;i+=(i&(-i))) add_in(rt[i],1,max(num1,num2),p,v);
}
ll query_out(int x,int l,int r){
ll ret=0;
for(int i=x;i;i&=(i-1)) ret+=query_in(rt[i],1,max(num1,num2),l,r);
return ret;
}
ll query(int l1,int r1,int l2,int r2){return query_out(r1,l2,r2)-query_out(l1-1,l2,r2);}
} t_s,t_r,t_m,T_r,T_m;
set<int> fst;
struct event{
int l,r,t;
event(int _l=0,int _r=0,int _t=0):l(_l),r(_r),t(_t){}
bool operator <(const event &rhs) const{return l<rhs.l;}
};
set<event> st;
void split(int x){
if(st.empty()) return;
set<event>::iterator it=st.lower_bound(event(x+1,0,0));
if(it==st.begin()) return;
--it;event val=*it;if(val.r<=x) return;
st.erase(st.find(val));
st.insert(event(val.l,x,val.t));
st.insert(event(x+1,val.r,val.t));
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d%d",&s[i],&mx[i],&r[i]);
for(int i=1;i<=n;i++){
if(r[i]){
lim1[i]=mx[i]/r[i];
lim2[i]=(mx[i]-s[i])/r[i];
} else lim1[i]=lim2[i]=INF;
}
for(int i=1;i<=n;i++) key1[i]=lim1[i],key2[i]=lim2[i];
sort(key1+1,key1+n+1);sort(key2+1,key2+n+1);key1[0]=-INF;key2[0]=-INF;
for(int i=1;i<=n;i++) if(key1[i-1]!=key1[i]) uni1[++num1]=key1[i];
for(int i=1;i<=n;i++) if(key2[i-1]!=key2[i]) uni2[++num2]=key2[i];
for(int i=1;i<=n;i++){
lim1[i]=lower_bound(uni1+1,uni1+num1+1,lim1[i])-uni1;//remember to unique
lim2[i]=lower_bound(uni2+1,uni2+num2+1,lim2[i])-uni2;
// printf("%d %d\n",lim1[i],lim2[i]);
}
for(int i=1;i<=n;i++) fst.insert(i);
for(int i=1;i<=n;i++){
t_s.add_out(i,lim2[i],s[i]);
t_r.add_out(i,lim2[i],r[i]);
t_m.add_out(i,lim2[i],mx[i]);
} int qu;scanf("%d",&qu);
while(qu--){
int t,L,R;scanf("%d%d%d",&t,&L,&R);
int pos=lower_bound(uni2+1,uni2+num2+1,t)-uni2;
ll sums=t_s.query(L,R,pos,num2);
ll sumr=t_r.query(L,R,pos,num2);
ll summ=t_m.query(L,R,1,pos-1);
// printf("%d %lld %lld %lld\n",pos,sums,sumr,summ);
// printf("%lld\n",summ+sums+sumr*t);
ll sum=summ+sums+sumr*t;
if(L!=1) split(L-1);split(R);
while(1){
set<event>::iterator it=st.lower_bound(event(L,0,0));
if(it==st.end()||(it->l)>R) break;event val=*it;
int pp=lower_bound(uni1+1,uni1+num1+1,t-val.t)-uni1;
ll ssumr=T_r.query(val.l,val.r,pp,num1);
ll ssumm=T_m.query(val.l,val.r,1,pp-1);
// printf("%d %d %d %d\n",val.l,val.r,t-val.t,pp);
// printf("%lld %lld\n",ssumr,ssumm);
sum+=ssumm+ssumr*(t-val.t);st.erase(st.find(val));
} st.insert(event(L,R,t));
while(1){
set<int>::iterator it=fst.lower_bound(L);
if(it==fst.end()||(*it)>R) break;
int x=(*it);
t_s.add_out(x,lim2[x],-s[x]);
t_r.add_out(x,lim2[x],-r[x]);
t_m.add_out(x,lim2[x],-mx[x]);
T_r.add_out(x,lim1[x],r[x]);
T_m.add_out(x,lim1[x],mx[x]);
fst.erase(fst.find(x));
} printf("%lld\n",sum);
}
return 0;
}
/*
10
0 4 2
0 3 0
0 96 7
0 25 9
0 90 4
0 93 5
0 87 3
0 58 3
0 65 8
0 30 0
10
3 3 4
10 1 7
11 8 9
19 4 9
28 3 10
33 5 10
38 4 5
45 5 7
48 5 9
58 1 4
*/
Codeforces 453E - Little Pony and Lord Tirek(二维线段树+ODT)的更多相关文章
- codeforces 677D D. Vanya and Treasure(二维线段树)
题目链接: D. Vanya and Treasure time limit per test 1.5 seconds memory limit per test 256 megabytes inpu ...
- CodeForces 242E - XOR on Segment 二维线段树?
今天练习赛的题....又是线段树的变换..拿到题我就敲了个点更新区间查询的..果断超时...然后想到了可以将每个数与合表示成不进位的二进制数..这样就可以区间进行更新了..比赛的时候写搓了..刚重写了 ...
- CodeForces 242E二维线段树
E. XOR on Seg ...
- 【Codeforces Round #433 (Div. 1) C】Boredom(二维线段树)
[链接]我是链接 [题意] 接上一篇文章 [题解] 接(点我进入)上一篇文章. 这里讲一种用类似二维线段树的方法求矩形区域内点的个数的方法. 我们可以把n个正方形用n棵线段树来维护. 第i棵线段树维护 ...
- POJ2029 二维线段树
Get Many Persimmon Trees POJ - 2029 Seiji Hayashi had been a professor of the Nisshinkan Samurai Sch ...
- UVA 11297 线段树套线段树(二维线段树)
题目大意: 就是在二维的空间内进行单个的修改,或者进行整块矩形区域的最大最小值查询 二维线段树树,要注意的是第一维上不是叶子形成的第二维线段树和叶子形成的第二维线段树要 不同的处理方式,非叶子形成的 ...
- POJ2155 Matrix二维线段树经典题
题目链接 二维树状数组 #include<iostream> #include<math.h> #include<algorithm> #include<st ...
- HDU 1823 Luck and Love(二维线段树)
之前只知道这个东西的大概概念,没具体去写,最近呵呵,今补上. 二维线段树 -- 点更段查 #include <cstdio> #include <cstring> #inclu ...
- poj 2155:Matrix(二维线段树,矩阵取反,好题)
Matrix Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 17880 Accepted: 6709 Descripti ...
随机推荐
- .Net Core微信服务商二次进件
最近商城进行微信服务商二次进件的开发,大致有几个点 一,服务商签名 二,服务商证书获取 三,图片上传 四,敏感信息加密 五,查询进件状态 除此之外,就是进件信息的拼装 电商二级商户进件申请单-状态流转 ...
- MySQL:提高笔记-4
MySQL:提高笔记-4 学完基础的语法后,进一步对 MySQL 进行学习,前几篇为: MySQL:提高笔记-1 MySQL:提高笔记-2 MySQL:提高笔记-3 MySQL:提高笔记-4,本文 说 ...
- OO第三单元
OO第三单元 JML语言理论基础,应用工具链 JML语言基础 JML简介 定义: JML 是一种形式化的. 面向 JAVA 的行为接口规格语言 作用: 开展规格化设计.这样交给代码实现人员的将不是可能 ...
- Prometheus基于文件的服务发现
Prometheus基于文件的服务发现 一.基于文件的服务发现 1.prometheus.yml 配置文件的写法 2.file_sd 目录下的文件 3.配置结果 二.注意事项 三.参考链接 一.基于文 ...
- 编译qwt遇到的问题
在windows下使用mingw编译从git上下载的qwt工程下的tests时一直提示一下错误: error: undefined reference to `qMain(int, char**)' ...
- Linux Ubuntu stty 使用
stty(set tty)命令用于显示和修改当前注册的终端的属性. 该命令是一个用来改变并打印终端行设置的常用命令. stty -a #将所有选项设置的当前状态写到标准输出中 old_stty_set ...
- git安装心得
每天码代码打卡任务,老师需要我们提交链接,这就需要我们把自己打的代码文件上传到GitHub上来,以此获得链接. 自己是一个新人,安装git也是什么都不懂(跟着网上的教程也总是能出错) 安装正常操作:h ...
- 小米多模网关接入Home Assistant ZNDMWG03LM
一.小米zigbee网关使用 先下载米家app,打开手机蓝牙,登陆点"我的"界面,将网关设备插上电源,橙灯闪烁,点击蓝牙网关等待弹窗提示连接,选择连接路由器(需2.4GHz),输入 ...
- netfilter/iptables 学习
netfilter概述 netfilter 组件位于内核空间(kernelspace),是内核的一部分,由一些信息包过滤表组成,这些表包含内核用来控制信息包过滤处理的规则集. iptables 组件是 ...
- Matlab 中 arburg 函数的理解与实际使用方法
1. 理解 1.1 Matlab 帮助: a = arburg(x,p)返回与输入数组x的p阶模型相对应的归一化自回归(AR)参数. 如果x是一个向量,则输出数组a是一个行向量. 如果x是矩阵,则参数 ...