2019-2020 ICPC Asia Hong Kong Regional Contest : I. Incoming Asteroids (阈值优化)
题目大意:
给定n个星球,m次询问。询问有两种类型,第一种是加入一个人\(i\),并声明其总观测值\(y_i\),和需要观测的\(k_i\)个星球,直到观测时长之和达到\(y_i\);第二种是让所有需要观测第x个星球的同学,增加观测值y,并输出当前累积观测时长\(≥y\)的人的编号。
$ 1\leq n,m\leq2e5$ , \(0<k\leq3\)
解题思路:
由题意可知,人和星球之间是多对多的关系,不易用数据结构维护。但如果对于每一次询问直接去枚举每个人是否达到总观测值显然是不行的。考虑到k的取值范围非常小,根据抽屉原理,当某个人的总观测值达到y时,必定有一个星球的贡献达到了\(\lceil\frac{y}{k} \rceil\)。那么对于这个人所需观测的k个星球分别加入一个对应的增量阈值\(\Delta x=\frac{\delta}{k},\delta=总观测值-当前观测值\),每当某一星球的观测时长增量达到预设阈值,就判断对应的这个人的观测值是否已经达到y,如果没有达到,就根据新的\(\delta\)重新设置增量阈值,每次达到阈值,所增长的观测值至少为当前\(\delta\)的\(\frac{1}{3}\),因此复杂度是\(log_{\frac{3}{2}}y\)。那么如何统一的判断增量的变化呢,可以把阈值修改为 \(time_i+\Delta x\) ,\(time_i\)表示第 \(i\)个星球的从开始到现在的累积观测时长,对于每一个星球\(i\)建立一个小根堆\(q_i\),用来维护阈值,当\(time_i≥q_i.top()\),就表示达到了某一阈值,删除它并在此人对应的k个星球各自加入新的阈值。维护一个堆的复杂度是\(mlogm\),总复杂度为\(O(mlogm·log_{\frac{3}{2}}y)\)
代码
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
typedef pair<LL,LL> pLL;
typedef pair<double,double> pdd;
const int N=3e5+5;
const int M=65;
const LL inf=1e9;
const int mod=1e9+7;
const double eps=1e-8;
const long double pi=acos(-1.0L);
#define ls (i<<1)
#define rs (i<<1|1)
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define mk make_pair
#define mem(a,b) memset(a,b,sizeof(a))
LL read()
{
LL x=0,t=1;
char ch;
while(!isdigit(ch=getchar())) if(ch=='-') t=-1;
while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
return x*t;
}
struct node{
int id;
LL now;
friend bool operator < (node A,node B){
return A.now>B.now;
}
};
priority_queue<node> q[N];
vector<int> e[N];
int cnt,vis[N],las;
LL t[N],a[N];
void solve(int i)
{
vector<int> ans;
while(!q[i].empty())
{
if(t[i]<q[i].top().now) break;
int x=q[i].top().id;
q[i].pop();
if(vis[x]) continue;
LL res=a[x];
for(auto y:e[x]) res-=t[y];
if(res<=0) {
ans.eb(x);
vis[x]=1;
continue;
}
int d=(res+e[x].size()-1)/e[x].size();
for(auto y:e[x]) q[y].push(node{x,t[y]+d});
}
printf("%d",ans.size());
sort(ans.begin(),ans.end());
for(auto x:ans) printf(" %d",x);
printf("\n");
las=ans.size();
}
int main()
{
int n=read(),m=read();
for(int i=1;i<=m;i++)
{
int op=read();
if(op==1)
{
int y=read()^las;
a[++cnt]=y;
int k=read();
int d=(a[cnt]+k-1)/k;
while(k--) {
int x=read()^las;
e[cnt].eb(x);
a[cnt]+=t[x];
q[x].push(node{cnt,t[x]+d});
}
}else{
int x=read()^las;
int y=read()^las;
t[x]+=y;
solve(x);
}
}
return 0;
}