1. 程式人生 > 其它 >CodeForces - 1034D Intervals of Intervals

CodeForces - 1034D Intervals of Intervals

我是^(* ̄(oo) ̄)^!耶! 目錄

博主的 \(\rm bibi\) 時間

“不會還有人不知道區間 \([l,r]\) 的長度是 \(r-l\) 吧?”

題目

傳送門

解法

還是來理一理思路吧。

先考慮弱化版:詢問在 \([l,r]\) 中線段的並的長度。

\(1\)\(n\) 的順序加入線段,用 set 來維護。需要維護三個值 \((l,r,t)\),其中 \(l,r\) 是線段端點,\(t\)最新 覆蓋此區間的編號。需要注意的是,當 \(t\) 相等時必須保證線段不相交。

有什麼用呢?實際上我們預先按順序加入線段,用 vector 儲存每個時間包含的線段(還需要儲存上一個時間戳)。將詢問按右端點排序,假設當前詢問為 \([l,r]\)

,則計算第 \(r\) 個時間對應的線段。那麼 \(\le r\) 的條件一定滿足,於是定義 \(f(i)\) 為時間戳 \(\ge i\) 的線段並長度。思考一下線段 \([L,R]\) 的時間戳從 \(t\) 變到 \(t'\) 會有什麼影響:對於 \(i\in [t+1,t']\)\(f(i)\) 都加上了 \(R-L\)。這個可以差分一下。

如果強制線上呢?將差分陣列改成線段樹,再可持久化一下即可。


回到這道題,我們要求前 \(k\) 大區間的權值和(權值定義為線段的並的長度)。可以考慮二分第 \(k\) 大區間的權值,設當前二分到 \(\rm mid\),那麼問題轉化為查詢權值 \(\ge \rm mid\)

的區間的個數與權值和。需要注意的是可能有多個權值為第 \(k\) 大的區間,但由於已經知道這些區間的權值與個數,減去即可。另:如果查詢權值 \(>\rm mid\) 的區間的話,\(k-1\) 可能等於 \(0\),二分就會不停往大分。

在檢驗函式中,你會發現大區間的權值一定比它包含的小區間的權值大。基於此就可以用 \(\text{two-pointers}\) 查詢權值 \(\ge \rm mid\) 的區間:\(r\) 右移,計算第 \(r\) 個時間對應的線段。維護 \(l\) 為此情況下權值 \(\ge \rm mid\) 的最大左端時間,那麼 \([1,l]\) 都是合法的。

還有一些細節,程式碼加了註釋。

程式碼

#include <bits/stdc++.h>
using namespace std;

#define rep(i,_l,_r) for(signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y) 
#define debug(...) do {cerr<<__LINE__<<" : ("#__VA_ARGS__<<") = "; Out(__VA_ARGS__); cerr<<flush;} while(0)
template <typename T> void Out(T x) {cerr<<x<<"\n";}
template <typename T,typename ...I> void Out(T x,I ...NEXT) {cerr<<x<<", "; Out(NEXT...);}

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}

typedef long long ll;
typedef pair <ll,ll> pii;
#define int long long

const int maxn=3e5+5;

int n,m;
ll add[maxn];
struct node {
	int l,r,t;
	
	bool operator < (const node fk) const {
		return l<fk.l;
	}
};
set <node> s;
vector <pii> g[maxn];
typedef set <node> :: iterator Type;

Type split(int x) {
	Type it=s.lower_bound((node){x,0,0});
	if(s.end()!=it and it->l==x) return it;
	--it;
	if(it==s.end() or it->r==x) return s.end();
	node tmp=*it;
	s.erase(it);
	s.insert((node){tmp.l,x,tmp.t});
	return s.insert((node){x,tmp.r,tmp.t}).first;
}

pii ask(int x) {
	pii tt,ret; int L=0;
	ll sum=0,tmp=0;
	rep(i,1,n) add[i]=0;
	rep(i,1,n) {
		for(int j=0;j<g[i].size();++j) {
			tt=g[i][j];
			/*
				當 L>=tt.first 則 [tt.first,L] 一定滿足條件,先加入 sum,為避免重複不加入差分陣列
			*/
			if(tt.first>L)
				add[tt.first]+=tt.second;
			else {
				tmp+=tt.second;
				sum+=1ll*tt.second*(0ll+L-tt.first+1); 
			}
			add[i+1]-=tt.second;
		}
		while(L+1<=i and tmp+add[L+1]>=x) {
			tmp+=add[L+1];
			sum+=tmp; ++L;
		}
		ret.first+=L,ret.second+=sum;
	}
	return ret;
}

signed main() {
	n=read(9),m=read(9);
	s.insert((node){0,(int)1e9,0});
	rep(i,1,n) {
		int a=read(9),b=read(9);
		Type L=split(a),R=split(b); 
		while(L!=R) {
			g[i].push_back(make_pair(L->t+1,L->r-L->l));
			s.erase(L++);
		}
		s.insert((node){a,b,i});
	} 
	int l=0,r=1e9,mid; pii ret;
 	while(l<r) {
		mid=l+r+1>>1; 
		ret=ask(mid);
		if(ret.first>=m) l=mid;
		else r=mid-1;
	}
	ret=ask(l);
	print(ret.second-1ll*(ret.first-m)*l,'\n');
	return 0;
}