魔塔

BZOJ

设每个敌人的属性值为\(hp_i,atk_i,def_i\)。自己的为\(HP,ATK,DEF\)

首先我们可以发现顺序是没有影响的。

然后我们可以发现合适的\(ATK\)一定满足\(\max(hp_i+def_i)\ge ATK>\max(def)\),\(DEF\)一定满足\(DEF\le\max(atk_i)\)。

对于一个确定的\(ATK,DEF\),我们可以计算出\(HP=\sum(\lceil\frac{hp_i}{ATK-def_i}\rceil\max(0,atk_i-DEF))+1\)。

也就是说总费用是一个关于\(ATK,DEF\)的二元函数。

看到\(\lceil\frac{hp_i}{ATK-def_i}\rceil\)我们会马上想到除法分块,这东西最多有\(\sqrt{hp_i}\)个取值。因为所有的属性值都是\(1e6\)级别的,所以总的取值个数是\(n\sqrt{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\)递增,\(\lceil\frac{hp_i}{ATK-def_i}\rceil\)递减,也就是\(HP\)递减的速度越来越慢,即\(f(x)\)严格单调不增。

根据第一条性质,我们可以在固定一个\(ATK\)时,通过二分找到第一个\(f(x)<Cost_{DEF}\)的位置,从而找到最优的\(DEF\)。

根据第二条性质,当\(ATK\)增大时,第一个\(f(x)<Cost_{DEF}\)的位置一定只会向左移,我们可以维护一个单调指针来找到最优的\(DEF\)。

然后讲下如何具体维护\(f(x)\)。

当\(ATK\ from\ x-1\ to\ x\),会有部分\(\lceil\frac{hp_i}{ATK-def_i}\rceil\)发生\(-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

第一问随便做,把所有在这个方向的分速度为正的向量全部加上就行了。

第二问可以根据这个扩展一个写法。

随便钦定一个单位向量,把题目给的向量按在这个方向上的分量降序排序。

考虑我们旋转这个单位向量,这个次序会随之变化对吧,也就是有\(n\choose2\)个分界点,经过这个分界点时有两个向量的次序会交换。

假如有多个连续的向量同时交换,那么我们应该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\n",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\n",tmp);
for(int i=1;i<=n;++i) printf("%lld ",len(ans[i]));
}

THUSC2013的更多相关文章

  1. BZOJ4141 THUSC2013 魔塔 贪心

    没得传送门 考虑当\(Atk\)增大时,\(Def\)一定越来越没用,因为回合数在变少.所以考虑从小到大枚举\(Atk\)然后双指针计算. 设\(f_i(x)\)表示在\(Atk = i\)时,\(D ...

随机推荐

  1. 解决zabbix的cannot allocate shared memory of size错误

    问题状态:zabbix_server 不能启动,系统CentOS 6.7 原因分析:这是因为内核对share memory的限制造成的. 用到如下命令ipcs [-m|l|a],sysctl [-a| ...

  2. 二十九、SELinux简介

    一.基础 1)访问模型 Linux原有访问模型:自主访问控制 DAC 安全隐患: 进程所能访问资源的范围 为用户所能访问的资源范围 后门: rootkit程序 进程被胁持: 基于进程作为跳板,就有了进 ...

  3. linux C file format analysis

    c语言文件格式 source file file.c C source, ASCII text pretreatment 预处理文件 file.i C source, ASCII text assem ...

  4. Postgresql - MATERIALIZED VIEW

    MATERIALIZED VIEWPG 9.3 版本之后开始支持物化视图.View 视图:虚拟,不存在实际的数据,在查询视图的时候其实是对视图内的表进行查询操作. 物化视图:实际存在,将数据存成一张表 ...

  5. PostgreSQL判断一个表是否存在

    postgresql判断一个表是否存在: 方法一: select count(*) from pg_class where relname = 'tablename'; 方法二: select cou ...

  6. Ubuntu: Linux下查看本机显示器分辨率(xrandr)

    版权声明:转载请注明出处 https://blog.csdn.net/JNingWei/article/details/75044598   Linux下查看本机显示器分辨率: $ xrandr Sc ...

  7. Bootstrap form-group and form-control

    https://github.com/twbs/bootstrap/blob/21f3375f21e9a7a5155d0cd783fd2bc7aeee8485/scss/_forms.scss htt ...

  8. Java同步数据结构之CopyOnWriteArrayList/CopyOnWriteArraySet

    前言 前面介绍完了队列(包括双端队列),今天探讨以下Java并发包中一个List的并发数据结构实现CopyOnWriteArrayList,顾名思义CopyOnWriteArrayList也是一种基于 ...

  9. mvc 接收json 集合 实例

    开始测试了一下,后台用实体类接收,所报异常如下 无奈之下只能传为字符串,然后字符串转json 页面代码如下 后台controller如下:

  10. 分布式存储——Build up a High Availability Distributed Key-Value Store

    原文链接 Preface There are many awesome and powerful distributed NoSQL in the world, like Couchbase, Mon ...