Codeforces Round #456 (Div. 2) 题解
Codeforces Round #456 (Div. 2)
Codeforces 912C
题意
本题题意较为复杂,是以MOBA游戏作为背景的:直接放链接吧
解题思路
其实本题的Note就是对解决本题的绝佳提示,我们可以用扫描线来处理该区域能击杀多少个英雄,该区域的最佳使用大招的时间就是该区域结束的时间。
需要注意的是判断最优价值无限大的条件:
1.价值增长速率不为\(0\)
2.有英雄最大生命值小于等于大招的伤害 或 有英雄生命恢复速率为0,并且初始生命值小于大招的伤害
实现上,用set
或者优先队列管理时间线,用两个map
分别管理某个时间点能击杀的英雄增加了多少,和减少了多少
AC代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
typedef pair<int,int> pii;
set <long long> timeline;
map <long long,int> erased,added;
vector <pii> enemy[maxn];
int regen[maxn],maxhealth[maxn],n,m,bounty,increase,damage;
int main(int argc, char const *argv[])
{
scanf("%d%d",&n,&m);
scanf("%d%d%d",&bounty,&increase,&damage);
for (int i=1;i<=n;i++)
{
int sh;
scanf("%d%d%d",&maxhealth[i],&sh,®en[i]);
enemy[i].push_back(make_pair(0,sh));
}
for (int i=1;i<=m;i++)
{
int t,e,h;
scanf("%d%d%d",&t,&e,&h);
enemy[e].push_back(make_pair(t,h));
}
for (int i=1;i<=n;i++)
{
vector<pii> &v=enemy[i];
sort(v.begin(),v.end());
if(increase&&(damage>=maxhealth[i]||(!regen[i]&&v[0].first<=damage))){
//cout<<(int)(damage>=maxhealth[i])<<(int)(!regen[i]&&v[0].first<=damage)<<endl;
puts("-1");
return 0;
}
for (int j=0;j<v.size();j++)
{
if(v[j].second>damage) continue;
long long nxtpos=2e9;
if(regen[i]) nxtpos=min(nxtpos,1ll*(damage-v[j].second)/regen[i]);
if(j!=v.size()-1) nxtpos=min(nxtpos,1ll*(v[j+1].first-v[j].first-1));
added[v[j].first]++;
erased[v[j].first+nxtpos]++;
timeline.insert(v[j].first);
timeline.insert(v[j].first+nxtpos);
}
}
long long ans=0,cnt=0;
for (auto i: timeline){
cnt+=added[i];
ans=max(ans,cnt*(bounty+1ll*increase*i));
cnt-=erased[i];
}
cout<<ans<<endl;
return 0;
}
Codeforces 912D
题意
给定\(n \times m\) 的鱼塘和\(r \times r\) 的渔网,投放\(k\)条鱼,求能捕到鱼数最大的期望
解题思路
首先不难想到转化为投放在某个位置的期望:$ \frac{\text{这个位置能被多少渔网覆盖}}{r\times r}$
进而贪心地选择期望最大的\(k\)个位置就行了,但如果要求出所有\(n \times m\)个值进而求出前\(k\)个值是过不了的,不过可以发现更“靠近”中心的位置更优(显然的),所以我们从中心位置出发做\(BFS\),在已扩展的点中选择最优值就可以了。
这题的教训:map
的去重不是看数据内容是否相同的,也不是要重载==
运算符,而是一般要对象及对象中的属性对象的<
操作符进行重载,重载的<
操作应满足“严格弱排序”。map并不是用==
操作符来判断两个对象是否相等的,而是两个对象相互都不“小于”时认为两个对象相等。
复杂度\(O(k \log k)\)
AC代码
#include <bits/stdc++.h>
using namespace std;
int n,m,r,k;
long long solve(int i,int j)
{
int tx=min(n,i+r-1)-max(i-r+1,1)-r+2;
int ty=min(m,j+r-1)-max(j-r+1,1)-r+2;
return 1ll*tx*ty;
}
struct node
{
int x,y,z;
node(int _x=1,int _y=1):x(_x),y(_y){z=solve(x,y);};
bool operator < (const node& rhs) const
{
return z<rhs.z;
}
};
int dx[]={-1,1,0,0},dy[]={0,0,-1,1};
map < pair<int,int> , bool > vis;
priority_queue<node> Q;
long long bfs()
{
Q.push(node((n+1)/2,(m+1)/2));
vis[make_pair((n+1)/2,(m+1)/2)]=true;
long long res=0;
while (k--)
{
node u=Q.top();
Q.pop();
res+=u.z;
for (int i=0;i<4;i++){
node v=node(u.x+dx[i],u.y+dy[i]);
if(vis[make_pair(v.x,v.y)]) continue;
if(v.x<1||v.x>n||v.y<1||v.y>m) continue;
vis[make_pair(v.x,v.y)]=true;
Q.push(v);
}
}
return res;
}
int main(int argc, char const *argv[])
{
cin>>n>>m>>r>>k;
printf("%.10f\n",1.0*bfs()/(1.0*(n-r+1)*(m-r+1)));
}
Codeforces 912E
题意
给定素数集合\(p\),\(Card\ p \le 16\),\(p_i \le 100\),设集合\(S=\text{素因子只在}p\text{中的集合}\), 求\(S\)中第\(k\)大的数,保证这个数\(\le 1e18\)
解题思路
如果直接求\(S\),复杂度难以接受,这里我们将集合\(p\)拆成\(A\)和\(B\),那么\(S\)中的每个元素都是\(A\)和\(B\)中元素的积,可以通过双指针法来求\(S\)中小于某个数有多少个值,进而我们可以通过二分法求出答案。
时间复杂度\(O(\log\ 1e18\ (S_A+S_B) )\)
对于\(A,B\)的选择,不是简单地分成两份,因为素因子小的集合产生的数多,经测试,排序后将前6个数分出来最坏情况下两个集合大小都不超过\(1e6\),其他的数都不够优
AC代码
#include <bits/stdc++.h>
using namespace std;
int prime[20],n,k;
const long long maxnum=1e18;
vector <long long> A,B;
void dfs(long long value,int from,int to,vector<long long> &out)
{
out.push_back(value);
for (int i=from;i<=to;i++)
if(maxnum/prime[i]>=value)
dfs(value*prime[i],i,to,out);
}
long long solve(long long x)
{
int j=0;
long long res=0;
for (int i=A.size()-1;i>=0;i--)
{
while(j<B.size()&&B[j]<=x/A[i])
j++;
res+=j;
}
return res;
}
int main(int argc, char const *argv[])
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&prime[i]);
sort(prime+1,prime+n+1);
scanf("%d",&k);
dfs(1,1,min(6,n/2),A);
dfs(1,min(n/2,6)+1,n,B);
sort(A.begin(),A.end());
sort(B.begin(),B.end());
//cout<<A.size()<<" "<<B.size()<<endl;
long long l=1,r=maxnum,mid,ans;
while(l<=r)
{
mid=(l+r)>>1;
if(solve(mid)>=k)
{
ans=mid;
r=mid-1;
}
else
l=mid+1;
}
cout<<ans<<endl;
return 0;
}