2024CCPC哈爾濱部分題解

长皆發表於2024-10-30

賽時被評測機卡死了
M.奇怪的上取整

\(\sum_{i=1}^{n} f(n,i)\)
\(Input\)
第一行一個整數\(T(1<=T<=10^3)\),表示資料組數
對於每組資料,一行一個整數\(n(1<=n<=10^9)\)
\(Output\)
對於每組資料,輸出一行一個整數,表示答案。
\(Sample\)
3
5
451
114514
————————
21T
10251
7075858
思路:如果\(i\)不是\(n\)的因數,顯然\(i\)會減少到成為\(n\)的因數為止,隨後返回\(n/i\),這個對於\(i\)等於\(1或2\)也成立.
所以直接分解出\(n\)的因數\(\sqrt n\),然後\(sort\)一下\(nlogn\),遍歷一遍即可,總時間複雜度\((\sqrt n+nlogn)*T\)左右
注意:\((\sqrt n+nlogn)*T\)最壞接近\(10^{8.5}\),不能用\(map\)

	#include<iostream>
	#include<queue>
	#include<map>
	#include<set>
	#include<vector>
	#include<algorithm>
	#include<deque>
	#include<cctype>
	#include<string.h>
	#include<math.h>
	#include<time.h>
	#include<random>
	#include<stack>
	#include<string>
	#define ll                                long long 
	#define lowbit(x) (x & -x)
	using namespace std;
	const ll mod=1e9+7;
	ll ksm(ll x,ll y)
	{
		ll ans=1;
		while(y)
		{
			if(y&1)
			{
				ans=ans%mod*(x%mod)%mod;
			}
			x=x%mod*(x%mod)%mod;
			y>>=1;
		}
		return ans;
	}
	ll gcd(ll x, ll y)
	{
		if (y == 0)
			return x;
		else
			return gcd(y, x % y);
	}
	void fio()
	{
		ios::sync_with_stdio(0);
		cin.tie(0);
		cout.tie(0);
	}
	ll a[3250000];
	int main()
	{
		fio();
		ll t;
		cin>>t;
		//map<ll,bool>q;
		while(t--)
		{
			//q.clear();
			ll n;
			cin>>n;
			ll cnt=0;
			ll u=n;
			for(ll i=1;i*i<=n;i++)
			{
				if(n%i==0)
				{
					cnt++;
					a[cnt]=i;
					if(n/i==i)
					continue;
					else 
					{ 
						cnt++;
						a[cnt]=n/i;
					}
				}
			}
			sort(a+1,a+1+cnt);
			ll ans=0;
			for(ll i=cnt;i>=1;i--)
			{
				if(n>=a[i])
				{
					ans+=(n-a[i]+1)*(u/a[i]);
					n=a[i]-1;
				}
			}
			ans+=n*u;
			cout<<ans<<endl;
		}

	}

C.在哈爾濱指路
在哈爾濱問路,把行人給的指路方向和位移,轉換成你自己的行動方向和位移
\(Input\)
第一行一個整數\(T(1<=T<=10^4)\)
對於每組資料,第一行一個整數\(n(1<=n<=10)\),表示指路指令的個數
接下來\(n\)行,每行按照絕對位置描述一個指令,包含一個字元 \(d(d\in\{N,S,W,E\})\) 和一個整數 \(x(1<=x<=10)\)
表示「往\(d\)方位走到第\(x\)個路口」。其中\(N\)表示向北,\(S\)表示向南,\(W\)表示向西,\(E\)表示向東,保證相鄰兩個指令中d
不相同且不相反(北與南互相相反,西與東互相相反).
\(Output\)
對於每組資料,第一行輸出一個整數\(m(1<=m<=20)\)和一個字元\(f(f\in\{N,S,W,E\})\),分別表示按哈爾濱人習慣的指路方式的指令條數和初始面向的方位,方位的含義同輸入中描述
接下來輸出\(m\) 行,每行首先輸出一個字元\(g\in\{Z,L,R\}\),其中\(Z\)表示直走,\(L\)表示左轉,\(R\)表示右轉。如果輸出的字元為\(Z\),此行還需輸出一個整數\(y(1<y<100)\)
表示直走到第\(y\)個路口。第一個輸出的指令必須以\(Z\)開頭,輸出中相鄰兩個指令的字元\(g\) 不能相同,並且\(L\)指令和\(R\)指令不能相鄰
本題中你無需最小化\(m\),如果有多種方案可以到達同一目的地,輸出任意一個均可。
\(Sample\)
1
2
S 2
E 1
——————————
3 S
Z 2
L
Z 1
思路:本題說那麼多就是告訴你它會給出一個人在上北下南左西有東為方位時的走路狀況,你只要把他變成第一人稱運動按照它給的路徑行動即可
暴力模擬,資料給的很寬裕
注意:目前可用的評測機如果endl不用換行符代替,一定會\(TLE\)

	#include<iostream>
	#include<queue>
	#include<map>
	#include<set>
	#include<vector>
	#include<algorithm>
	#include<deque>
	#include<cctype>
	#include<string.h>
	#include<math.h>
	#include<time.h>
	#include<random>
	#include<stack>
	#include<string>
	#define ll                                long long 
	#define lowbit(x) (x & -x)
	#define endl "\n"
	using namespace std;
	const ll mod=1e9+7;
	ll ksm(ll x,ll y)
	{
		ll ans=1;
		while(y)
		{
			if(y&1)
			{
				ans=ans%mod*(x%mod)%mod;
			}
			x=x%mod*(x%mod)%mod;
			y>>=1;
		}
		return ans;
	}
	ll gcd(ll x, ll y)
	{
		if (y == 0)
			return x;
		else
			return gcd(y, x % y);
	}
	void fio()
	{
		ios::sync_with_stdio(0);
		cin.tie(0);
		cout.tie(0);
	}
	pair<char,ll>ans[32];
	vector<pair<char,ll>>g;
	ll pd(char x)
	{
		if(x=='S')
		return 1;
		else if(x=='E')
		return 2;
		else if(x=='N')
		return 3;
		else 
		return 4;
	}
	int main()
	{
		fio();
		ll t;
		cin>>t;
		while(t--)
		{
			g.clear();
			ll n;
			cin>>n;
			ll l=0;
			for(ll i=1;i<=n;i++)
			{
				cin>>ans[i].first>>ans[i].second;
				ll j=pd(ans[i].first);
				if(j==l)
				{
					g.push_back({'Z',ans[i].second});
					continue;
				}
				if(l==0)
				{
					if(j==1)
					{
						l=j;
						g.push_back({'S',0});
					}
					else if(j==2)
					{
						l=j;
						g.push_back({'E',0});
					}
					else if(j==3)
					{
						l=j;
						g.push_back({'N',0});
					}
					else 
					{
						l=j;
						g.push_back({'W',0});
					}
					g.push_back({'Z',ans[i].second});
				}
				else if(l==1)
				{
					if(j==2)
					{
						g.push_back({'L',0});
						g.push_back({'Z',ans[i].second});
					}
					else 
					{
						g.push_back({'R',0});
						g.push_back({'Z',ans[i].second});
					}
				}
				else if(l==2)
				{
					if(j==3)
					{
						g.push_back({'L',0});
						g.push_back({'Z',ans[i].second});
					}
					else 
					{
						g.push_back({'R',0});
						g.push_back({'Z',ans[i].second});
					}
				}
				else if(l==3)
				{
					if(j==4)
					{
						g.push_back({'L',0});
						g.push_back({'Z',ans[i].second});
					}
					else 
					{
						g.push_back({'R',0});
						g.push_back({'Z',ans[i].second});
					}
				}
				else if(l==4)
				{
					if(j==1)
					{
						g.push_back({'L',0});
						g.push_back({'Z',ans[i].second});
					}
					else 
					{
						g.push_back({'R',0});
						g.push_back({'Z',ans[i].second});
					}
				}
				l=j;
			}
			cout<<(ll)g.size()-1<<" ";
			ll k=0;
			for(auto j:g)
			{
				k++;
				if(k==1)
				cout<<j.first<<endl;
				else 
				{
				if(j.second==0)
				cout<<j.first<<endl;
				else 
					cout<<j.first<<" "<<j.second<<endl;
				}
			}
		}

	}

G.歡迎加入線上會議!

你想在 MeLink 上組織一次有 \(n\) 位參會者的線上會議,參會者編號為 \(1\)\(n\)。對於這 \(n\) 位參會者的每一位,都至少認識一位除了自己之外的參會者,認識關係是雙向的。

會議的組織過程如下:首先由一個人建立會議並加入。隨後,已經進入會議的成員可以拉一些自己認識但還沒入會的參會者入會,直到所有 \(n\) 位參會者都入會。但是有 \(k\) 位參會者正忙著除錯程式,這些人可以被拉進會議,但不會建立會議或拉他認識的人入會。

你希望確定是否有可能讓所有 \(n\) 位成員都入會。如果可行,請確定拉人入會的方案。

$Input$

第一行三個整數 \(n, m, k\) (\(2 \le n \le 2 \times 10^5\), \(1 \le m \le \min\{5 \times 10^5, \frac{n(n-1)}{2}\}\), \(0 \le k \le n\)),分別表示參會者人數,互相認識的關係數和目前正忙的人數。

第二行 \(k\) 個整數 \(a_1, \ldots, a_k\) (\(1 \le a_i \le n\)),其中第 \(i\) 個整數表示第 \(a_i\) 位成員正忙。這些整數兩兩不同。如果 \(k=0\),這一行將為空,但不會省略。

接下來的 \(m\) 行中,每行兩個整數 \(p_i\)\(q_i\) (\(1 \le p_i, q_i \le n\), \(p_i \neq q_i\)),表示 \(p_i\)\(q_i\) 相互認識。認識關係是雙向的。保證同一認識關係不會重複出現,且每個人都至少認識另一個人。

$Output$

如果無法組織有這 \(n\) 位成員參加的會議,則在第一行輸出 \(\texttt{No}\)

如果可以,則在第一行輸出 \(\texttt{Yes}\)。接下來,在第二行輸出一個整數 \(t\) (\(1 \le t \le n\)),表示組織該會議所需的步驟數。

接下來 \(t\) 行,每行描述組織該會議的一步。在第 \(j\) 行,首先輸出一個整數 \(x_j\) (\(1 \leq x_j \leq n\))。如果 \(j=1\),則 \(x_j\) 表示建立會議的成員,否則,\(x_j\) 必須是已經被拉入會議的一位成員。所有的 \(x_j\) 應兩兩不同。接下來,輸出一個整數 \(y_j\) (\(1 \leq y_j \leq n\)),表示 \(x_j\)\(y_j\) 個成員入會。最後,輸出 \(y_j\) 個整數 \(z_l\) (\(1 \leq z_l \leq n\)),表示被 \(x_j\) 拉入會議的成員編號。\(z_l\) 應當兩兩不同,並且整個過程中同一個人不能多次被拉入會。

你不必最小化 \(t\),輸出任意一種合法方案均可。

\(Sample1\)
4 5 2
3 4
1 2
1 3
2 3
3 4
2 4
————————————
Yes
2
1 2 2 3
2 1 4

\(Sample2\)
4 5 3
2 4 3
1 2
1 3
2 3
3 4
2 4
——————————
No
思路:其實給的是張圖,這個圖有\(k\)個點不能拉人入會,但是可以被拉入會,而其他的點可以進行傳播擴散拉人入會
由於多個輸出還得是會中人作為邀請方,加上拉朋友只能拉建立變的,所以考慮用\(bfs\)進行擴散
首先對於只能被邀請的點進行\(vis\)標記,然後遍歷所有點,找到第一個不被標記的點,以它為圓心擴散
如果一個數的相鄰數被打上\(vis\)了,就不能讓它作為邀請方,否則進入\(vector\).但是如果一個數有沒被邀請入會,我該怎麼統計?這時考慮用第二個\(vi\)標記
來作為是否被拉入會的標誌,這樣可以避免重複
用兩個\(vector\)分別儲存邀請方和被邀請方即可得出答案
注意:如果所有人一開始都是被邀請方,或者\(bfs\)走一遍後,沒有所有點被\(vis\)標記過,得輸出\(No\)

	#include<iostream>
	#include<queue>
	#include<map>
	#include<set>
	#include<vector>
	#include<algorithm>
	#include<deque>
	#include<cctype>
	#include<string.h>
	#include<math.h>
	#include<time.h>
	#include<random>
	#include<stack>
	#include<string>
	#define ll                            long long 
	#define lowbit(x) (x & -x)
	#define endl "\n"
	#define ll  long long 
	using namespace std;
	const ll mod=1e9+7;
	ll ksm(ll x,ll y)
	{
		ll ans=1;
		while(y)
		{
			if(y&1)
			{
				ans=ans%mod*(x%mod)%mod;
			}
			x=x%mod*(x%mod)%mod;
			y>>=1;
		}
		return ans;
	}
	ll gcd(ll x, ll y)
	{
		if (y == 0)
			return x;
		else
			return gcd(y, x % y);
	}
	void fio()
	{
		ios::sync_with_stdio(0);
		cin.tie(0);
		cout.tie(0);
	}
	vector<ll>g[250000],ans[250000];
	bool vis[250000];
	bool vi[250000];
	vector<ll>uo;
	void bfs(ll x)
	{
		queue<ll>q;
		q.push(x);
		while(!q.empty())
		{
			ll x=q.front();
			q.pop();
			if(vis[x])continue;
			uo.push_back(x);
			vis[x]=1;
			vi[x]=1;
			for(auto j:g[x])
			{
				if(vi[j]==0)
				{
					vi[j]=1;
					ans[x].push_back(j);
				}
				if(vis[j]==0)
				{
					q.push(j);
				}
			}
		}
	}
	int main()
	{
		fio();
		ll n,m,k;
		cin>>n>>m>>k;
		for(ll i=1;i<=k;i++)
		{
			ll x;cin>>x;
			vis[x]=1;
		}
		while(m--)
		{
			ll l,r;
			cin>>l>>r;
			g[l].push_back(r);
			g[r].push_back(l);
		}
		ll s=0;
		for(ll i=1;i<=n;i++)
		{
			if(!vis[i])
			{
				s=i;
				break;
			}
		}
		if(s==0)
		{
			cout<<"No"<<endl;
			return 0;
		}
		bfs(s);
		ll cnt=0;
		for(ll i=1;i<=n;i++){
			if(vi[i])cnt++;
		}
		if(cnt!=n)
		{
			cout<<"No"<<endl;
			return 0;
		}
		cnt=0;
		for(ll i=1;i<=n;i++)
		{
			if(ans[i].size()>0)cnt++;
		}
		cout<<"Yes"<<endl;
		cout<<cnt<<endl;
		for(auto i:uo)
		{
			if(ans[i].size()==0)continue;
			cout<<i<<" "<<ans[i].size()<<" ";
			for(auto j:ans[i])
			{
				cout<<j<<" ";
			}
			cout<<endl;
		}
	}

k.農場經營

你放棄了程式設計,來到了三江平原開始務農。在勞動過程中你改掉了作息不規律的毛病,每天你都恰好工作 \(m\) 個單位時間。現在到了收穫的季節,你需要收割並加工你種植的 \(n\) 種作物,對於第 \(i\) 種作物,處理一單位時間該種作物將獲得 \(w_i\) 的收益。為了使每天的工作不會太單調,對於第 \(i\) 種作物,你每天處理它的總時間長度可以是 \([l_i, r_i]\) 範圍內的整數。

某天,天氣預報說第二天的天氣不好,於是在今天你需要調整時間安排以儘快搶收作物。具體地說,你能最多選擇一種作物,並刪除每天處理這種作物的時間範圍限制,即刪除後處理該作物的總時間長度可以是 \([0, m]\) 範圍內的任意整數,而處理其他作物的時間範圍不變。你仍然在這一天恰好工作 \(m\) 個單位時間。

你想知道滿足上述條件的情況下,這一天能獲得的最大收益是多少。

\(Input\)

第一行兩個整數 \(n\)\(m\) (\(1 \le n \le 10^5\), \(1 \le m \le 10^{11}\)),分別表示作物種類數和一天工作時間長度。

接下來 \(n\) 行,每行三個整數 \(w_i\), \(l_i\), 和 \(r_i\) (\(1 \le w_i \le 10^6\), \(1 \le l_i \le r_i \le 10^6\)),表示作物的收益和總時間長度的限制。

資料保證 \(\sum_{i=1}^n l_i \le m \le \sum_{i=1}^n r_i\)

\(Output\)

輸出一行一個整數,表示這一天能獲得的最大收益。

\(Sample\)
5 17
2 3 4
6 1 5
8 2 4
4 3 3
7 5 5
——————
109
思路:仔細想一下,不難發現刪除一個作物並沒一個準確的答案(值最大\(or\)最小),所以得列舉所有作物作為被刪除時的最大值
如何列舉?
不妨想想如果一個作物被刪除時(不妨設它單位時間價值為\(x\)),除了保證其他作物最x基本的搶收時間外,肯定是剩下時間用於搶收能搶收的最大值最賺,
於是引申出兩種狀況
1.滿足基本情況時,剩下時間用於搶收\(W_i\)比被x大的作物的時間總和小於他們的搶收時間最大值之和
這時一定會有\(W_i\)比x大的作物不存在實際搶收時間等於它的最大搶收時間,這時肯定是從右往左能搶收時間最大就最大,顯然有單調性,做兩個個字尾陣列分別儲存
\(i\)個作物的搶收時間最大時的價值之和,和最大搶收時間-最小搶收時間的和,這時可以二分答案哪裡要細處理,哪裡直接全拿,至此\(W_i\)比x的作物的價值和得出,\(W_i\)比x小的作物直接用最小搶收
時間的價值字首和陣列即可得出,至此答案得出
2.滿足基本情況時,剩下時間用於搶收\(W_i\)比被x大的作物的時間總和大於等於他們的搶收時間最大值之和
這時直接用字尾和陣列貪心全拿\(W_i\)比x大的作物價值之和,字首和陣列得出\(W_i\)比x小的作物價值之和,剩下時間全拿被刪除的作物

	#include<iostream>
	#include<queue>
	#include<map>
	#include<set>
	#include<vector>
	#include<algorithm>
	#include<deque>
	#include<cctype>
	#include<string.h>
	#include<math.h>
	#include<time.h>
	#include<random>
	#include<stack>
	#include<string>
	#define ll                            long long 
	#define lowbit(x) (x & -x)
	#define endl "\n"
	#define ll  long long 
	using namespace std;
	const ll mod=1e9+7;
	ll ksm(ll x,ll y)
	{
		ll ans=1;
		while(y)
		{
			if(y&1)
			{
				ans=ans%mod*(x%mod)%mod;
			}
			x=x%mod*(x%mod)%mod;
			y>>=1;
		}
		return ans;
	}
	ll gcd(ll x, ll y)
	{
		if (y == 0)
			return x;
		else
			return gcd(y, x % y);
	}
	void fio()
	{
		ios::sync_with_stdio(0);
		cin.tie(0);
		cout.tie(0);
	}
	struct s
	{
		ll w;
		ll l,r;
	}p[250000];
	bool cmp(s x,s y)
	{
		return x.w<y.w;
	}
	ll sub[250000];
	ll gs[250000];
	ll pre[250000];
	ll to[250000];
	ll op[250000];
	ll wz[250000];
	int main()
	{
	ll n,m;
	cin>>n>>m;	
	for(ll i=1;i<=n;i++)
	{
		ll w,l,r;
		cin>>w>>l>>r;
		p[i].w=w;
		p[i].l=l;
		p[i].r=r;
	}
	sort(p+1,p+1+n,cmp);
	for(ll i=n;i>=1;i--)
	{
		sub[i]=p[i].w*p[i].r+sub[i+1];
		gs[i]=gs[i+1]+p[i].r;
	}
	for(ll i=1;i<=n;i++)
	{
		pre[i]=pre[i-1]+p[i].l*p[i].w;
		//cout<<p[i].w<<endl;
		to[i]=to[i-1]+p[i].l;
	}
	ll ans=pre[n];
	//cout<<ans<<endl;
	for(ll i=1;i<=n;i++)
	{
		op[i]=op[i-1]+p[n-i+1].r-p[n-i+1].l;
		wz[i]=n-i+1;
	}
	for(ll i=1;i<=n;i++)
	{
		ll cnt=to[i-1]+gs[i+1];//個數
		if(cnt>m)
		{
			ll u=m-(to[n]-p[i].l);
			ll j=lower_bound(op+1,op+1+n,u)-op;
			j--;
			ll cnt1=m-(to[n]-p[i].l+op[j]);
			ans=max(ans,pre[wz[j+1]]-p[i].l*p[i].w+sub[wz[j]]+cnt1*(p[wz[j+1]].w));
		}
		else
		{
			ans=max(ans,sub[i+1]+pre[i-1]+(m-cnt)*p[i].w);
		}
	}
	cout<<ans<<endl;
	}

J.新能源汽車

有一輛新能源汽車,這輛車有 \(n\) 個電瓶,第 \(i\) 個電瓶容量為 \(a_i\) 單位,每消耗 \(1\) 單位電力能恰好前進 \(1\) 公里。車只能前進,不能反向行駛。你可以選擇汽車行駛的每一公里所使用的電力來自哪個電瓶。

汽車在出發前每個電瓶都是充滿電的。行駛中途會經過 \(m\) 個充電站,第 \(j\) 個充電站距離起點 \(x_j\) 公里,並且只能給第 \(t_j\) 個電瓶充電,每個充電站能提供的電力是無限的。

請計算這輛新能源汽車最遠可以行駛多少公里。

$Input$

第一行一個整數 \(T\) (\(1\le T\le 10^4\)),表示測試資料組數。

對於每組資料,第一行兩個整數 \(n, m\) (\(1\le n,m\le 10^5\)),表示汽車電瓶個數和充電站的個數。

第二行 \(n\) 個整數 \(a_1,a_2,\ldots,a_n\) (\(1\le a_i\le 10^9\)),分別表示每個電瓶的容量。

接下來 \(m\) 行,每行兩個整數 \(x_j, t_j\) (\(1\le x_j\le 10^9\), \(1\le t_j\le n\)),分別表示每個充電站的位置和它能給哪個電瓶充電。

對於每組測試資料,保證 \(1\le x_1<x_2<\ldots<x_m\le 10^9\)。所有測試資料的 \(n\) 之和與 \(m\) 之和均不超過 \(2\cdot 10^5\)

$Output$

對於每組資料,輸出一行一個整數,表示這輛車最遠可以行駛多少公里。

\(Sample\)
2
3 1
3 3 3
8 1
2 2
5 2
1 2
2 1
————————
12
9
思路:顯然最好的使用電池方法就是,下個充電站是哪個就用哪個電池,如果相應電池沒電力,用後面充電站可以充的電池繼續前行即可,不能充的電池最好留到最後再用
怎麼維護?
考慮開多維\(set\),這樣對於能充電的電池能儲存它對應的充電站位置,隨後開個\(set<pair<ll,ll>>\)前面儲存最近的充電位置,後面儲存對應的電池序號
如果一個電池無充電站了,就把他儲存起來,最後用,如果還有則用多維\(set\)儲存的最近衝電站位置在\(set<pair<ll,ll>>\)裡進行更新,隨後如此反覆即可
具體還得看看程式碼
注意:車到了充電站時得考慮目前使用得電池是否被用完

		#include<iostream>
		#include<queue>
		#include<map>
		#include<set>
		#include<vector>
		#include<algorithm>
		#include<deque>
		#include<cctype>
		#include<string.h>
		#include<math.h>
		#include<time.h>
		#include<random>
		#include<stack>
		#include<string>
		#define ll                            long long 
		#define lowbit(x) (x & -x)
		#define endl "\n"
		#define ll  long long 
		using namespace std;
		const ll mod=1e9+7;
		ll ksm(ll x,ll y)
		{
			ll ans=1;
			while(y)
			{
				if(y&1)
				{
					ans=ans%mod*(x%mod)%mod;
				}
				x=x%mod*(x%mod)%mod;
				y>>=1;
			}
			return ans;
		}
		ll gcd(ll x, ll y)
		{
			if (y == 0)
				return x;
			else
				return gcd(y, x % y);
		}
		void fio()
		{
			ios::sync_with_stdio(0);
			cin.tie(0);
			cout.tie(0);
		}
		ll a[325000];
		ll b1[325000];//位置
		ll b2[325000];//幾號電池
		map<ll,ll>mp;//還有多少個位置點
		set<ll>q[325000];//下個位置點
		set<pair<ll,ll>>f;//使用組
		//set<pair<ll,ll>>cd;
		ll d[3250000];
		int main()
		{
		fio();
		ll t;
		cin>>t;
		while(t--)
		{
			f.clear();
			mp.clear();
			ll n,m;
			cin>>n>>m;
			for(ll i=1;i<=n;i++)
			{
				cin>>a[i];
				d[i]=a[i];
				q[i].clear();
			}
			for(ll i=1;i<=m;i++)
			{
				ll x,y;
				cin>>b1[i]>>b2[i];
				q[b2[i]].insert(b1[i]);
				mp[b2[i]]++;
			}
			ll cn=0;
			for(ll i=1;i<=n;i++)
			{
				if(mp[i]==0)cn+=a[i];
				else f.insert({*q[i].begin(),i});
			}
			b1[m+1]=1e18;
			ll l=0;//包括自己
			ll wz=1;//位置指標
			while(1)
			{
				//討論還有的
				if((ll)f.size()>0)
				{
				ll su=l;
				l=min(l+d[(*f.begin()).second],b1[wz]);
				ll k=(*f.begin()).second;d[k]-=(l-su);
				ll cs=(*f.begin()).first;
				if(b1[wz]==l)wz++;
				f.erase(*f.begin());
				if(l==cs)
				{
					d[k]=a[k];mp[k]--;
					q[k].erase(cs);
					if(mp[k]>0)
					{
						f.insert({*q[k].begin(),k});
					}
					else cn+=d[k];
				}
				else 
				{
					if(l==b1[wz-1])
					{
					if(d[k]>0)
					{
						f.insert({*q[k].begin(),k});
					}
					ll c=b2[wz-1];
					d[c]=a[c];mp[c]--;
					q[c].erase(l);
					if(mp[c]>0)
					{
						f.insert({*q[c].begin(),c});
					}
					else cn+=d[c];
					}
				}
				}
				else 
				{
					ll su=l;
					l=min(l+cn,b1[wz]);
					cn-=(l-su);
					if(l!=b1[wz])break;
					wz++;
					ll c=b2[wz-1];
					d[c]=a[c];mp[c]--;
					q[c].erase(l);
					if(mp[c]>0)
					{
						f.insert({*q[c].begin(),c});
					}
					else cn+=d[c];
				}
			}
			cout<<l<<endl;
		}
		}

相關文章