[JLOI2015] 城池攻占

题目

原题地址

解说

首先发现乘的时候 系数不会为负,所以能得到一个关键条件:变化后的战斗力随变化前的战斗力大小单调
所以我们考虑倍增
设hp[x][i]是从x开始一路攻克(2^i)个城池所需要最小的初始生命值
设trans[x][i][0/1]是攻克了(2^i)个城池后攻击力的变化量,0表示乘,1表示加,先乘后加
注意乘的系数初始化成1
然后就可以倍增了
然而空间大小恶意卡倍增
但是我们这个倍增可以换成三进制的
就是把定义里的(2^i)都换成(3^i)
于是(log_3300000=10),空间就变成了原来的一半
需要注意的一点是,最后在跳倍增的时候,同一个长度可以跳两次(因为是三进制嘛),循环的时候稍微注意一下

代码

#include<bits/stdc++.h>
#define pa pair<ll,ll>
#define CLR(a,x) memset(a,x,sizeof(a))
#define MP make_pair
using namespace std;
typedef long long ll;
const int maxn=3e5+5;

inline char gc(){
	return getchar();
	static const int maxs=1<<16;static char buf[maxs],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,maxs,stdin),p1==p2)?EOF:*p1++;
}
inline ll rd(){
    ll x=0;char c=gc();bool neg=0;
    while(c<'0'||c>'9'){if(c=='-') neg=1;c=gc();}
    while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+c-'0',c=gc();
    return neg?(~x+1):x;
}

int N,M,fa[maxn][11];
ll hp[maxn][11],trans[maxn][11][2];
int ans1[maxn],ans2[maxn];
int pw3[11];

int main(){
    //freopen("","r",stdin);
    int i,j,k;
    pw3[0]=1;for(i=1;i<=10;i++) pw3[i]=pw3[i-1]*3;
    N=rd(),M=rd();
    for(i=1;i<=N;i++) hp[i][0]=rd();
    fa[1][0]=N+1;
    for(i=2;i<=N;i++){
    	fa[i][0]=rd();
    	ll x=rd(),y=rd();
    	trans[i][0][0]=1;
    	trans[i][0][!x]=y;
    	for(j=0;j<10;j++){
    		int f=fa[i][j],ff=fa[f][j];
    		if(!f||!ff||!fa[ff][j]) break;
    		fa[i][j+1]=fa[ff][j];
    		ll hp1=max(hp[f][j],(ll)ceil(1.0*(hp[ff][j]-trans[f][j][1])/trans[f][j][0]));
    		hp[i][j+1]=max(hp[i][j],(ll)ceil(1.0*(hp1-trans[i][j][1])/trans[i][j][0]));
    		trans[i][j+1][1]=trans[i][j][1]*trans[f][j][0]+trans[f][j][1];
    		trans[i][j+1][0]=trans[i][j][0]*trans[f][j][0];
    		trans[i][j+1][1]=trans[i][j+1][1]*trans[ff][j][0]+trans[ff][j][1];
    		trans[i][j+1][0]=trans[i][j+1][0]*trans[ff][j][0];
    	}
    }
    for(i=1;i<=M;i++){
    	ll s=rd();int x=rd(),n=0;
    	for(j=10;j>=0&&x!=-1;j--){
    		if(fa[x][j]&&hp[x][j]<=s){
    			s=s*trans[x][j][0]+trans[x][j][1];
    			x=fa[x][j];
    			n+=pw3[j];j++;
    		}
    	}
    	if(x!=-1) ans1[x]++;
    	ans2[i]=n;
    }
    for(i=1;i<=N;i++)
    	printf("%d
",ans1[i]);
    for(i=1;i<=M;i++)
    	printf("%d
",ans2[i]);
    
    return 0;
}

幸甚至哉,歌以咏志。

原文地址:https://www.cnblogs.com/DarthVictor/p/12928854.html