• THUSC2013


    魔塔

    BZOJ
    设每个敌人的属性值为(hp_i,atk_i,def_i)。自己的为(HP,ATK,DEF)
    首先我们可以发现顺序是没有影响的。
    然后我们可以发现合适的(ATK)一定满足(max(hp_i+def_i)ge ATK>max(def))(DEF)一定满足(DEFlemax(atk_i))
    对于一个确定的(ATK,DEF),我们可以计算出(HP=sum(lceilfrac{hp_i}{ATK-def_i} ceilmax(0,atk_i-DEF))+1)
    也就是说总费用是一个关于(ATK,DEF)的二元函数。
    看到(lceilfrac{hp_i}{ATK-def_i} ceil)我们会马上想到除法分块,这东西最多有(sqrt{hp_i})个取值。因为所有的属性值都是(1e6)级别的,所以总的取值个数是(nsqrt{1e6})级别的。当然我们的(ATK)最大只能取到(2e6)
    所以我们考虑,假如我们固定了一个(ATK),设(f(x))表示此时(DEF from x-1 to x)(-Delta HP)(f(x))显然是一个分段函数。
    如果你对“(ATK-DEF)”这类的计算公式有一定了解的话,可以直接得出下面两个结论:
    (1.f(x))严格单调不增。
    (2.)随着(ATK)的增加,(f(x))严格单调不增。
    一句话理解就是(ATK,DEF)都具有边界效应。
    详细点讲的话,
    (1.)(DEF)增大时,(max(0,atk_i-DEF))会有越来越多的取到(0),也就是(HP)递减的速度越来越慢,即(f(x))严格单调不增。
    (2.)随着(ATK)递增,(lceilfrac{hp_i}{ATK-def_i} ceil)递减,也就是(HP)递减的速度越来越慢,即(f(x))严格单调不增。
    根据第一条性质,我们可以在固定一个(ATK)时,通过二分找到第一个(f(x)<Cost_{DEF})的位置,从而找到最优的(DEF)
    根据第二条性质,当(ATK)增大时,第一个(f(x)<Cost_{DEF})的位置一定只会向左移,我们可以维护一个单调指针来找到最优的(DEF)
    然后讲下如何具体维护(f(x))
    (ATK from x-1 to x),会有部分(lceilfrac{hp_i}{ATK-def_i} ceil)发生(-1)的变化从而导致(f(x))变化。
    观察可以发现这个变化相当于对(f(x))的一段前缀减一个数。并且根据上文除法分块部分的分析总的变化次数大概是(1e7)级别的。
    因为最优(DEF)是单调左移的,所以我们可以直接把前缀减反映到差分数组上(即减的最后一位),在(DEF)指针左移时直接计算影响,如果这个前缀超过了(DEF),那么超过(DEF)的部分是没有影响的。
    然后这题卡空间,需要用链表/前向星。

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    int read(){int x;scanf("%d",&x);return x;}
    const int N=2000007;
    int n,ca,cd,mx,ATK,DEF,head[N],ver[N*5],Next[N*5],edge[N*5],tot;ll a[N],ans,sum,HP,X,Y,Z;
    void add(int x,int y,int id){ver[++tot]=x,Next[tot]=head[id],edge[tot]=y,head[id]=tot;}
    void update(int p,int v){a[p]+=v;if(Y<=p)sum+=v,Z+=(p-Y)*v;}
    int main()
    {
        n=read(),ca=read(),cd=read(),ans=1e18,Y=1e6,Z=1;
        for(int i=1,l,hp,atk,def;i<=n;++i)
        {
    	hp=read()-1,atk=read(),def=read();
    	for(l=1;l<=hp;l=(hp/(hp/l))+1) if(l==1) add(atk,hp+1,def+1); else add(atk,hp/l-hp/(l-1),def+l);
    	add(atk,-1,hp+def+1),mx=max(mx,def);
        }
        for(X=1;X<=2e6;++X)
        {
    	for(int i=head[X];i;i=Next[i]) update(ver[i],edge[i]);
    	if(X<=mx) continue;
    	while(Y>1&&sum<cd) Z+=sum,sum+=a[--Y];
    	if(Z+X*ca+Y*cd<=ans) ans=Z+X*ca+Y*cd,ATK=X,DEF=Y,HP=Z;
        }
        printf("%lld %d %d",HP,ATK,DEF);
    }
    

    宇宙飞艇

    BZOJ
    第一问随便做,把所有在这个方向的分速度为正的向量全部加上就行了。
    第二问可以根据这个扩展一个写法。
    随便钦定一个单位向量,把题目给的向量按在这个方向上的分量降序排序。
    考虑我们旋转这个单位向量,这个次序会随之变化对吧,也就是有(nchoose2)个分界点,经过这个分界点时有两个向量的次序会交换。
    假如有多个连续的向量同时交换,那么我们应该reverse而不是一个个swap。(可以自己画图模拟)
    可以发现最优的答案一定会在分界点取到。
    那么我们顺时针枚举一遍,维护次序数组,每次取在这个方向上分量为正的就行了,最后去最大值。
    假如强制选(k)个,那就取当前次序前(k)个就行了。

    #include<bits/stdc++.h>
    #define ll long long
    #define ld long double
    using namespace std;
    int read(){int x;scanf("%d",&x);return x;}
    ll max(ll a,ll b){return a>b? a:b;}
    const int N=1007;
    int n,m,id[N],pos[N],bd[N];vector<int>in;
    struct vec{ll x,y;ld arg;vec(int a=0,int b=0):x(a),y(b),arg(atan2(b,a)){}}a[N],ans[N],sum[N],q;
    ll len(vec&a){return a.x*a.x+a.y*a.y;}
    vec operator+(vec&a,vec&b){return vec(a.x+b.x,a.y+b.y);}
    ll operator*(vec&a,vec&b){return a.x*b.x+a.y*b.y;}
    int operator==(vec&a,vec&b){return a.x*b.y==a.y*b.x;}
    struct line{int a,b;vec x;}b[N*N];
    void max(vec&a,vec&b){if(len(b)>len(a))a=b;}
    int main()
    {
        n=read(),q.x=read(),q.y=read();ll tmp=0;
        for(int i=1;i<=n;++i) a[i].x=read(),a[i].y=read(),tmp+=max(0ll,a[i]*q),id[i]=i;
        printf("%lld
    ",tmp);
        for(int i=1,j;i<=n;++i) for(j=i+1;j<=n;++j) b[++m]={i,j,vec(a[i].y-a[j].y,a[j].x-a[i].x)},b[++m]={i,j,vec(a[j].y-a[i].y,a[i].x-a[j].x)};
        sort(b+1,b+m+1,[&](const line&a,const line&b){return a.x.arg<b.x.arg;});
        sort(id+1,id+n+1,[](int i,int j){return a[i].x<a[j].x||(a[i].x==a[j].x&&a[i].y<a[j].y);});
        for(int i=1;i<=n;++i) ans[i]=sum[i]=sum[i-1]+a[id[pos[id[i]]=i]];
        for(int l=1,r,x,y,i;l<=m;l=r)
        {
    	r=l,in.clear();
    	for(;r<=m&&b[r].x==b[l].x;++r)
    	{
    	    x=pos[b[r].a],y=pos[b[r].b];
    	    if(x>y) swap(x,y);
    	    if(!bd[x]) in.push_back(x);
    	    bd[x]=max(bd[x],y);
    	}
    	sort(in.begin(),in.end());
    	for(int x:in)
    	{
    	    if(!bd[x]) continue;
    	    reverse(id+x,id+(y=bd[x])+1);
    	    for(i=x;i<=y;++i) pos[id[i]]=i,bd[i]=0,max(ans[i],sum[i]=sum[i-1]+a[id[i]]);
    	}
        }
        tmp=0;
        for(int i=1;i<=n;++i) tmp=max(tmp,len(ans[i]));
        printf("%lld
    ",tmp);
        for(int i=1;i<=n;++i) printf("%lld ",len(ans[i]));
    }
    
  • 相关阅读:
    10年后编程还有意义吗?
    专访Jeffrey Richter:Windows 8是微软的重中之重
    x86汇编指令脚本虚拟机
    基于容器的持续交付管道
    NET Core 整合Autofac和Castle
    数据结构与算法1
    Redis集群
    react + iscroll5
    MongoDB
    WebComponent
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12017778.html
Copyright © 2020-2023  润新知