CF1993C Light Switches 題解

Sorato發表於2024-08-05

CF1993C Light Switches 題解

題目大意

\(n\) 盞燈,第 \(i\) 盞燈亮著的時間為 \([a_i+bk,a_i+(b+1)k-1]\),其中 \(k\) 為給定常數,\(b\) 為任意非負偶數。求一個最小的 \(t\),使得在時間 \(t\) 所有燈都是亮著的。

Solve

\(m=2k\),顯然所有燈的開關狀態以 \(m\) 為週期,所以我們考慮把所有燈的開關狀態對映到 \([0,m)\),並求出每個燈對映的偏移量 \(d_i\),即需要往前推多少個週期才能把開關狀態對映到 \([0,m)\),顯然有 \(d_i=\lfloor{a_i\over m}\rfloor\),那麼對映後,這盞燈開著的區間即為 \([a_i\bmod m,a_i\bmod m+k-1]\)

對於答案,取 \(\min\limits_{cnt_t=n}\{t+mx_t\times m\}\),其中 \(t=0,1,\dots,m-1\)\(cnt_t\) 為在 \(t\) 時刻為開著的燈的個數,\(mx_t\) 為在 \(t\) 時刻為開著的燈的偏移量的最大值。

但有一些細節。考慮有一些燈的開關狀態在 \([0,m)\) 內可能是 11100011 這樣的,\(1\) 在兩邊。這時候左邊的 \(1\) 的偏移量為 \(\lfloor{a_i\over m}\rfloor+1\),右側的 \(1\) 的偏移量為 \(\lfloor{a_i\over m}\rfloor\)

至於具體怎麼維護 \(cnt_t\)\(mx_t\),顯然可以上資料結構比如線段樹,但這裡提供一種不用資料結構的方法。

考慮借用掃描線的思想,對於每個 \(a_i\),將對應區間左端點的 \(cnt\) 加上 \(1\),右端點的 \(cnt\) 減去 \(1\)(相當於差分),遍歷 \(t\) 時字首和統計即可。而對於 \(mx\),考慮在區間左端點插入二元組 \((d_i,1)\),右端點插入二元組 \((d_i,0)\)。遍歷 \(t\) 時,先操作 \(t\) 上的所有二元組,開一個 set,若二元組第二維是 \(1\),則將 \(d_i\) 加入,否則刪除。那麼 \(mx_t\) 即為 set 的末尾元素。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
	short f=1;
	int x=0;
	char c=getchar();
	while(c<'0'||c>'9')	{if(c=='-')	f=-1;c=getchar();}
	while(c>='0'&&c<='9')	x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
const int N=2e5+10;
int t,n,k,c[N<<1],ans,m;
struct zzn{int x;bool op;};
vector<zzn>q[N<<1];
multiset<int>s;//注意可能有相等的元素,故需要multiset
signed main()
{
	t=read();
	while(t--)
	{
		n=read();k=read();ans=1e18;m=k<<1;
		s.clear();
		for(int i=0;i<m;i=-~i)	c[i]=0,q[i].clear();
		for(int i=1,a;i<=n;i=-~i)
		{
			a=read();
			c[a%m]++,q[a%m].push_back({a/m,1});
			if(a%m+k<m)
				c[a%m+k]--,q[a%m+k].push_back({a/m,0});
			else//對應上文中 1 在左右兩側的情況
				c[0]++,q[0].push_back({a/m+1,1}),
				c[(a%m+k)%m]--,q[(a%m+k)%m].push_back({a/m+1,0});
		}
		for(int i=0,cnt=0;i<m;i=-~i)
		{
			cnt+=c[i];
			for(zzn j:q[i])
			{
				if(j.op)	s.insert(j.x);
				else	s.erase(s.find(j.x));//相等的元素只刪除一個
			}
			if(cnt==n)	ans=min(ans,i+(*--s.end())*m);
		}
		if(ans==1e18)	puts("-1");
		else	printf("%lld\n",ans);
	}
	return 0;
}

相關文章