CF1271D Portals

杂题CF1271D Portals (数据较原题加强)

题目大意

最开始,你掌控着一只有 k k k 个人的军队,你的敌人有 n n n 个城堡。

为了占领第 i i i 个城堡,你需要至少 a i a_i ai 个士兵(你游戏玩的很好,所以你并不会损失士兵)。

当你占领了第 i i i 个城堡后,你可以扩军。其中在第 i i i 个城堡,你可以得到 b i b_i bi 个士兵。

此外,在你占领了一个城堡后,你可以派至少一个兵驻守。每个城堡有一个重要值 c i c_i ci ,你的总分就是所有你派兵驻守了的城堡的 c i c_i ci 的和。

有两种方式派兵:

  • 你刚占领这个城堡,可以立马派兵驻守。

  • m m m 条小路,其中第 i i i 条是从 u u u 通往 v v v 。保证 u > v u>v u>v 。你可以使用这条小路当且仅当你当前在 u u u ,且拥有至少一个士兵可以派往 v v v

显然,如果一个士兵被派去驻守,他就离开了你的军队。

你需要按照从 1 1 1 n n n 的顺序依次攻打。

假设你当前在 i i i ,当你攻占 i + 1 i+1 i+1 时,所有在 i i i 处的小路就不能用了。

如果在游戏中你没有足够的士兵去攻打下一座城堡,你就输了。

你的目标是在胜利的前提下最大化得分。

数据范围

1 ≤ n ≤ 3 ∗ 1 0 5 , 0 ≤ m ≤ min ⁡ ( n ( n − 1 ) 2 , 3 ⋅ 1 0 5 ) , 0 ≤ k ≤ 1 0 9 1\leq n\leq 3*10^5,0\leq m\leq \min(\frac{n(n-1)}{2},3\cdot 10^5),0\leq k\leq 10^9 1n3105,0mmin(2n(n1),3105),0k109

解题思路

我们考虑贪心,显然对于一个城市我们肯定在最后可以控制它的城市再控制它最优

我们可以对于每个城市都在最后再强行控制它,但这样可能导致失败,怎么办呢?

考虑反悔贪心,我们可以把所有已选的 c i c_i ci加到一个小根堆堆里,每次如果当前兵力小于等于 a i a_i ai就弹出对顶,同时补充一个士兵,这样一定是正确的

复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

代码

#include<bits/stdc++.h>
using namespace std;
int i,j,n,m,k,l,o,p;
int f[100005],a[100005],b[100005],c[100005];
int heap[100005],now;
struct node
{
	int to,nxt;
}side[100005];
int st[100005];
inline void up(int w)
{
	while (w>1&&heap[w]<heap[w/2]) 
	{
		swap(heap[w],heap[w/2]);
		w/=2;
	}
}
inline void down(int w)
{
	while (w*2<=now)
	{
		int y=w*2;
		if (y!=now)
		{
			if (heap[y]>heap[y+1]) y++;
		}
		if (heap[w]>heap[y]) 
		{
			swap(heap[w],heap[y]);
			w=y;
		}
		else break;
	}
}
inline void ins(int num)
{
	heap[++now]=num;
	up(now);
}
inline int pop()
{
	if (now==0) return 0;
	int q=heap[1];
	heap[1]=heap[now];
	--now;
	down(1);
	return q;
}
inline int read()
{
	int f(1),x(0);
	char ch=getchar();
	while (!isdigit(ch)) {if (ch=='-') f*=-1;ch=getchar();}
	while (isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	return f*x;
}
inline void writ(int x)
{
	if (x<0) putchar('-'),x=-x;
	if (x/10) writ(x/10);
	putchar((char)(x%10+'0'));
}
inline void write(int x)
{
	writ(x);
	putchar(' ');
}
inline void add(int u,int v)
{
	side[i].to=v,side[i].nxt=st[u],st[u]=i;
}
int main()
{
	n=read(),m=read(),k=read();
	for (i=1;i<=n;i++) a[i]=read(),b[i]=read(),c[i]=read(),f[i]=i;
	for (i=1;i<=m;i++) o=read(),p=read(),f[p]=max(f[p],o);
	for (i=1;i<=n;i++) add(f[i],i);
	for (i=1;i<=n;i++)
	{
		while (k<a[i]&&now!=0) ++k,o=pop();
		if (k<a[i]) 
		{
			printf("-1");
			return 0;
		}
		k+=b[i];
		for (j=st[i];j;j=side[j].nxt) ins(c[side[j].to]),--k;
		while (k<0) ++k,o=pop();
	}
	p=0;
	while (now>0) o=pop(),p+=o;
	printf("%d",p);
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值