The 2024 ICPC Asia EC Regionals Online Contest (II) - Problem H. Points Selection

Claris發表於2024-09-21

注意到如果 $\text{query}(a,b,c)$ 為真,那麼 $\text{query}(\geq a,\geq b,c)$ 一定為真。

從小到大列舉詢問中 $a$ 的值,按橫座標從小到大依次加入每個點,維護 $f_c$ 表示最小的 $b$ 滿足 $\text{query}(a,b,c)$ 為真。

假設當前正在加入點 $(x,y,w)$,有 $f_{(c+w)\bmod n}=\min(f_{(c+w)\bmod n},\max(f_c,y))$。

觀察狀態轉移方程可知,如果 $y\geq f_{(c+w)\bmod n}$,那麼就沒有更新的必要。令 $m=\max(f_0,f_1,\dots,f_{n-1})$,那麼如果 $y\geq m$,則這個點是完全無用的,可以直接跳過。

由於資料隨機,可以近似地認為加入 $k$ 個點後,所有 $2^k$ 個子集和模 $n$ 的結果在 $[0,n)$ 等機率均勻分佈。可以看作 $2^k$ 個小球隨機放入 $n$ 個洞之中,填滿所有 $n$ 個洞所需的期望球數是 $O(n\log n)$,因此 $k=O(\log n)$ 時期望可以填滿整個 $f$ 陣列。這說明,$f$ 陣列的最大值的期望值等於所有已經加入的點的縱座標的第 $O(\log n)$ 小值,即 $O(\frac{n\log n}{k})$。

於是,加入的第 $k$ 個點的縱座標 $y<m$ 的機率為 $O(\frac{\log n}{k})$,期望只需要更新 $O(\sum_{k=1}^n\frac{\log n}{k})=O(\log^2n)$ 遍 $f$ 陣列。由於這個值並不大,每次暴力 $O(n)$ 更新整個陣列即可。有了 $f$ 陣列就可以很容易地求出最終的答案。

時間複雜度 $O(n\log^2n)$。

#include<iostream>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
const int N=500005;
int n,i,j,mx,f[N],g[N];ull suf[N],sum,ans;
struct E{int x,y,v;}e[N];
inline bool cmp(const E&a,const E&b){return a.x<b.x;}
inline void insert(int y,int v){
  if(y>=mx||v==n)return;
  for(int i=0;i<n;i++)g[i]=f[i];
  for(int i=0,j=v;i<n;){
    int tmp=max(f[i],y);
    if(tmp<g[j])g[j]=tmp;
    i++;
    j++;
    if(j==n)j=0;
  }
  mx=0;
  sum=0;
  for(int i=0;i<n;i++){
    f[i]=g[i];
    sum+=i*suf[f[i]];
    if(f[i]>mx)mx=f[i];
  }
}
int main(){
  ios_base::sync_with_stdio(0);cin.tie(0);
  cin>>n;
  for(i=1;i<=n;i++)cin>>e[i].x>>e[i].y>>e[i].v;
  sort(e+1,e+n+1,cmp);
  for(i=n;i;i--)suf[i]=suf[i+1]+i;
  for(i=0;i<n;i++)f[i]=n+1;
  f[0]=0;
  mx=n+1;
  for(i=j=1;i<=n;i++){
    while(j<=n&&e[j].x<=i){
      insert(e[j].y,e[j].v);
      j++;
    }
    ans+=sum*i;
  }
  cout<<ans;
}

  

相關文章