Educational Codeforces Round 155 (Rated for Div. 2) F. Last Man Standing(ST表/dp的松弛思想)

题目

t(t<=10)组样例,每次给出n(n<=2e5)个人,

第i个人有hi(hi<=2e5)条命,每条命有ai(ai<=2e5)点血

当对人造成x点伤害时,如果x<当前血量,血量减少x,否则,扣掉一条命,令血量回满

游戏开始时,你可以设置一个正整数x,

然后每一轮,对所有玩家都造成x点伤害,

当所有玩家阵亡时,游戏结束,游戏结束时结算得分

最后阵亡的玩家,假设在第a轮阵亡,而倒数第二个阵亡的玩家,假设在第b轮阵亡,

则最后阵亡的玩家的分数为a-b(a=b时,得分为0),其他玩家的分数为0

对于每个玩家i,独立地求,正整数x任意设置时,玩家i能获得的最大得分

思路来源

jiangly代码/nanami代码

题解

题解1(ST表)

简单手玩后发现, 每个人能坚持的轮数是h_{i}*\left \lceil \frac{a_{i}}{x}\right \rceil

所以,可以枚举每个x,对于x倍数枚举y,

对于[1,y]内的值v来说,\left \lceil \frac{v}{y} \right \rceil是相同的,区间求一下h的最大值(需要值对应的所在位置)

区间求最大值,st表O(nlogn)预处理一下,然后O(1)求,总复杂度O(nlogn)

题解2(后缀+松弛思想)

仔细思考后发现,ST表的过程是没有必要的,

实际只需要从大到小扫一遍,维护后缀最大值即可

因为一个值(hi,ai),

在小于ai的地方被枚举到的话,只会不超过答案

而在ai的地方被枚举到的时候,等于答案的值可以取到,

也就意味着可以取到最大值,

所以,扫两遍,第一遍求最大值,第二遍求次大值

代码1(ST表)

#include
using namespace std;
#define ll long long
#define MP make_pair
mt19937 rnd(time(0));
const int MAXN=4e5+5;
const int lim=2e5;
ll a[MAXN],h[MAXN],n,lg[MAXN];
ll ans[MAXN];
struct node{int x,y;}s[20][MAXN];
node operator +(node x,node y){
	for(int i:{y.x,y.y})if(i&&i!=x.x&&i!=x.y){
		if(h[x.x]>n;
	memset(ans,0,sizeof(ans));
	for(int i=1;i<=n;i++) cin>>h[i];
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;ival(mx1)) mx2=mx1,mx1=j;
				else if(val(j)>val(mx2)) mx2=j;
			}
		}
//		cout<>1]+1;
	int _;cin>>_;
	while(_--) solve();
}

代码2(后缀+松弛思想)

#include
//#include
//#include
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair P;
#define fi first
#define se second
#define dbg(x) cerr<<(#x)<<":"< dist(l, r); return dist(gen); }
const int N=2e5+10,M=2e5;
int t,n,h[N],a[N];
ll ans[N];
arraymx[N];
mapid;
void init(){
    rep(i,1,M){
        mx[i][0]=mx[i][1]=P(0,0);
        ans[i]=0;
    }
    id.clear();
}
int main(){
	sci(t);
	while(t--){
        sci(n);
        init();
        rep(i,1,n)sci(h[i]);
        rep(i,1,n){
            sci(a[i]);
            P x=P(h[i],a[i]);
            id[x]=i;
            rep(j,0,1){
                if(x>mx[a[i]][j]){
                    swap(x,mx[a[i]][j]);
                }
            }
        }
        per(i,M-1,1){
            rep(k,0,1){
                P x=mx[i+1][k];
                rep(j,0,1){
                    if(x>mx[i][j]){
                        swap(x,mx[i][j]);
                    }
                }
            }
        }
        rep(x,1,M){
            ll v1=-1,v2=-1,fir=-1,sec=-1;
            int p=-1;
            for(int i=1;i<=M;i+=x){
                ll w=1ll*mx[i][0].fi*(i+x-1)/x;
                if(w>v1)v1=w,p=i;
            }
            fir=v1;
            for(int i=1;i<=M;i+=x){
                ll w=1ll*mx[i][i<=p && mx[i][0]==mx[p][0]].fi*(i+x-1)/x;
                if(w>v2)v2=w;
            }
            sec=v2;
            if(fir>sec){
                p=id[mx[p][0]];
                ans[p]=max(ans[p],fir-sec);
            }
        }
        rep(i,1,n){
            printf("%lld%c",ans[i]," \n"[i==n]);
        }
    }
    return 0;
}

你可能感兴趣的:(思维题,#,st表,松弛,dp)