ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF1651F Tower Defense

2022-07-07 16:32:39  阅读:158  来源: 互联网

标签:rs int -- mid ++ Defense ls CF1651F Tower


一、题目

点此看题

二、解法

考虑颜色段均摊,维护场上的若干个连续段 \([l,r]\),可以按照左端点降序排列,这样询问时类似弹栈做就行了。

如果遇到 \(l=r\),这代表了一个单点,可以直接暴力计算。遇到 \(l<r\),这代表被以前的询问推平的一段区间,问题可以转化成给定初始生命 \(hp\),给定时间差 \(T\),给定左右端点 \([l,r]\),问是否能再次推平这个区间,或者是在这个区间停下。

关键在于在某个时间差 \(T\) 下计算 \([l,r]\) 之间的权值和。每个塔关于时间的权值可以看成一个分段函数,当 \(T\leq \lfloor\frac{c}{r}\rfloor\) 是斜率为 \(r\),截距为 \(0\) 的一次函数;当 \(T>\lfloor\frac{c}{r}\rfloor\) 是一个截距为 \(c\) 的常函数。

由于 \(t_i\leq 2\cdot 10^5\),我们可以预处理以位置为下标的可持久化线段树,这样每个塔可以转化为两个单点修改,线段树上维护一次函数即可,合并就是直接加。

那么原来的问题可以用线段树上二分来解决,时间复杂度 \(O(n\log n)\)

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M = 200005;
const int N = 30*M;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,q,T,p,dt[M],c[M],s[M],tim[M],nc[M];
int ans,cnt,rt[M],k[N],b[N],ls[N],rs[N];
vector<int> v[M];
void build(int &x,int l,int r)
{
	x=++cnt;
	if(l==r) {k[x]=dt[l];b[x]=0;return ;}
	int mid=(l+r)>>1;
	build(ls[x],l,mid);
	build(rs[x],mid+1,r);
	k[x]=k[ls[x]]+k[rs[x]];
}
void ins(int &x,int y,int l,int r,int p)
{
	x=++cnt;k[x]=k[y];b[x]=b[y];
	ls[x]=ls[y];rs[x]=rs[y];
	if(l==r) {k[x]=0;b[x]=c[l];return ;}
	int mid=(l+r)>>1;
	if(mid>=p) ins(ls[x],ls[y],l,mid,p);
	else ins(rs[x],rs[y],mid+1,r,p);
	k[x]=k[ls[x]]+k[rs[x]];
	b[x]=b[ls[x]]+b[rs[x]];
}
int ask(int x,int l,int r,int L,int R)
{
	if(!x || l>R || L>r) return 0;
	if(L<=l && r<=R) return T*k[x]+b[x];
	int mid=(l+r)>>1;
	return ask(ls[x],l,mid,L,R)+ask(rs[x],mid+1,r,L,R);
}
void find(int x,int l,int r,int L,int &hp)
{
	int mid=(l+r)>>1;
	if(L<=l)
	{
		int w=T*k[x]+b[x];
		if(w<=hp) {p=r;hp-=w;return ;}
		if(l==r) return ;
		if(find(ls[x],l,mid,L,hp),p==mid)
			find(rs[x],mid+1,r,L,hp);
	}
	else if(mid<L) find(rs[x],mid+1,r,L,hp);
	else if(find(ls[x],l,mid,L,hp),p==mid)
		find(rs[x],mid+1,r,L,hp);
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		c[i]=read();dt[i]=read();int j=c[i]/dt[i]+1;
		if(j<M) v[j].push_back(i);
	}
	build(rt[0],1,n);
	for(int i=1;i<M;i++)
	{
		rt[i]=rt[i-1];
		for(int x:v[i]) ins(rt[i],rt[i],1,n,x);
	}
	q=read();s[0]=n+1;
	for(int i=n;i>=1;i--) s[++m]=i,nc[m]=c[i];
	while(q--)
	{
		int t=read(),h=read();
		while(m)
		{
			int i=s[m],j=0;T=t-tim[m];
			if(s[m-1]-i==1)//l==r
			{
				j=min(c[i],dt[i]*T+nc[m]);
				if(j>h) {nc[m]=j-h;tim[m]=t;break;}
				m--;h-=j;continue;
			}
			j=ask(rt[T],1,n,i,s[m-1]-1);
			if(j>h)
			{
				p=i-1;find(rt[T],1,n,i,h);p++;
				if(p==s[m-1]-1) m--;else s[m]=p+1;
				s[++m]=p;tim[m]=t;
				nc[m]=min(c[p],dt[p]*T)-h;break;
			}
			h-=j;m--;
		}
		if(!m) ans+=h;
		if(s[m]!=1) s[++m]=1,tim[m]=t,nc[m]=0;
	}
	printf("%lld\n",ans);
}

标签:rs,int,--,mid,++,Defense,ls,CF1651F,Tower
来源: https://www.cnblogs.com/C202044zxy/p/16455138.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有