JLOI2015/BZOJ4003 城池攻占

Description

  小铭铭最近获得了一副新的桌游,游戏中需要用m个骑士攻占n个城池。
  这n个城池用1到n的整数表示。除1号城池外,城池i会受到另一座城池fi的管辖,其中fi  每个城池有一个防御值hi,如果一个骑士的战斗力大于等于城池的生命值,那么骑士就可以占领这座城池;否则占领失败,骑士将在这座城池牺牲。占领一个城池以后,骑士的战斗力将发生变化,然后继续攻击管辖这座城池的城池,直到占领1号城池,或牺牲为止。
  除1号城池外,每个城池i会给出一个战斗力变化参数ai,vi。若ai=0,攻占城池i以后骑士战斗力会增加vi;若ai=1,攻占城池i以后,战斗力会乘以vi。
  注意每个骑士是单独计算的。也就是说一个骑士攻击一座城池,不管结果如何,均不会影响其他骑士攻击这座城池的结果。
  现在的问题是,对于每个城池,输出有多少个骑士在这里牺牲;对于每个骑士,输出他攻占的城池数量。

Input

  第1行包含两个正整数n,m,表示城池的数量和骑士的数量。
  第2行包含n个整数,其中第i个数为hi,表示城池i的防御值。
  第3到n+1行,每行包含三个整数。其中第i+1行的三个数为fi,ai,vi,分别表示管辖这座城池的城池编号和两个战斗力变化参数。
  第n+2到n+m+1行,每行包含两个整数。其中第n+i行的两个数为si,ci,分别表示初始战斗力和第一个攻击的城池。

Output

  输出n+m行,每行包含一个非负整数。其中前n行分别表示在城池1到n牺牲的骑士数量,后m行分别表示骑士1到m攻占的城池数量。

Sample Input

5 5 50 20 10 10 30 1 1 2 2 0 5 2 0 -10 1 0 10 20 2 10 3 40 4 20 4 35 5

Sample Output

2 2 0 0 0 1 1 3 1 1

Hint

【数据范围】
  对于20%的数据,0 < n,m <= 3000;
  对于20%的数据,保证对于所有的城池i,fi=i-1。
  对于20%的数据,保证对于所有的城池i,ai=0,vi=0。
  以上三类数据两两没有交集。
  对于100%的数据,1<=n,m<=300000,1<=fi;当ai=1时,vi>0;保证任何时候骑士战斗力值的绝对值不超过10^18。

网上全是可并堆写法,虽然我才写可并堆没多久,但是已经忘完了,而且感觉有点不好搞(网上的标好像很长)。于是我们来倍增操作一下。

f[i][j]表示从i开始攻略2^j个城池需要的最小攻击力,辅助数组g[i][j]维护两个标记sum和mul(类似于行星序列那道题的标记)。所以我们有f[i][j]=max(f[i][j-1],(f[fa[i][j-1][j-1]-sum)/mul)。

#include<bits/stdc++.h>
using namespace std;
const int Maxn=300005;
#define ll long long
vector<int>G[Maxn];
int N[Maxn],M[Maxn];
int n,m,p[25][Maxn];
ll a[Maxn],v[Maxn],mn[25][Maxn];
struct info{
	ll tag,mul;
	info(ll _tag=0,ll _mul=1){tag=_tag,mul=_mul;}
	info operator*(const info&rhs){return info(tag*rhs.mul+rhs.tag,mul*rhs.mul);}
}g[25][Maxn];
void dfs(int x){
	int maxlog=log2(n);
	g[0][x]=info(a[x]?0ll:v[x],a[x]?v[x]:1ll);
	for(int j=1;j<=maxlog;++j)if(~p[j-1][x]){
		p[j][x]=p[j-1][p[j-1][x]];
		g[j][x]=g[j-1][x]*g[j-1][p[j-1][x]];
		mn[j][x]=max(mn[j-1][x],(ll)ceil((mn[j-1][p[j-1][x]]-g[j-1][x].tag)/(1.0*g[j-1][x].mul)));
	}
	for(int i=0;i<G[x].size();++i)
		if(G[x][i]^p[0][x])dfs(G[x][i]);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%lld",&mn[0][i]);
	}
	memset(p,-1,sizeof(p));
	for(int i=2;i<=n;++i){
		scanf("%d%lld%lld",&p[0][i],&a[i],&v[i]);
		G[p[0][i]].push_back(i);
	}
	dfs(1);
	int maxlog=log2(n);
	for(int i=1;i<=m;++i){
		ll v;int x,ret=0;scanf("%lld%d",&v,&x);
		for(int j=maxlog;j>=0;--j)if(~p[j][x]&&v>=mn[j][x]){
			v=v*g[j][x].mul+g[j][x].tag;
			M[i]+=(1<<j);
			x=p[j][x];
		}
		if(~x&&v>=mn[0][x])++M[i];
		if(~x&&v<mn[0][x])++N[x];
	}
	for(int i=1;i<=n;++i)
		cout<<N[i]<<'\n';
	for(int i=1;i<=m;++i)
		cout<<M[i]<<'\n';
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值