2021年8月11日訓練筆記

就先從我本身的負責的題目開始吧(node

\(1.Codeforces\ \ CF1374E2\)​​ 解題報告

\(3\ \sec\ |\ 256\ MegaBytes\)c++

題面概要

給定 \(n\) 本書,每本書有一個閱讀時間,而且可能被甲、乙喜歡或不喜歡。要求選擇剛好 \(m\) 本書,使得兩人喜歡的書都很多於 \(k\) 本,在知足上述條件的前提下,要求閱讀時間最短。若是沒有可行方案輸出 \(-1\)數組

\(1\le k\le n\le 2\times 10^5,\ \ 1\le t_i\le 10^4\)app

解題過程

這題是 \(hard\ version\),比 \(easy\) 版本多了一個 \(m\) 的限制。考慮一下怎麼作是最優的。咱們先把全部的書分紅 \(4\) 大類:less

$i.\ $ 甲和乙都喜歡的,此時本書的標記應爲 \(11\)​​;spa

$ii.\ $​​ 只有甲喜歡的,此時本書的標記應爲 \(10\)​​;code

$iii.\ $​​​ 只有乙喜歡的,此時本書的標記應爲 \(01\)​​​;排序

$iiii.\ $​​​ 甲和乙都不喜歡的,此時本書的標記應爲 \(00\)​​​​;隊列

在讀入的時候能夠把這四種書分別放進 \(4\) 個優先隊列裏邊,按照 \(t_i\) 從小到大排序。get

以後咱們先在 \(i.\)​ 裏邊和 \(ii.+iii.\)​ 裏邊取時間較少的,而後判斷一下,若是 \(k\)​ 還有殘餘,說明咱們放上最好的 \(m\)​ 本書依然沒法知足條件 \(1\),此時直接輸出 \(-1\) 便可。不然,咱們看一下 \(m\) 是否還有富餘,若是有的話就在剩下全部書中取最小的,以及計算一下若是把一個已經取了的 \(i.\) 換成 \(ii.+iii.\) 是否合算。

最終代碼

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,k;
vector<int> t,a,b;
vector<pair<int,int> > sol,abqsol;
priority_queue<pair<int,int>,vector<pair<int,int> >, greater<pair<int,int> > > abq,aq,bq,nq;
int read()
{
	int x=0,f=1;
	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;
}
int solve()
{
	int res=0;
	while(m && k && !abq.empty())
	{
		res+=abq.top().first,m--,k--;
		abqsol.push_back(abq.top()),abq.pop();
	}
	while(m>1 && k && !aq.empty() && !bq.empty())
	{
		res+=aq.top().first+bq.top().first,m--,m--,k--;
		sol.push_back(aq.top()),sol.push_back(bq.top()),aq.pop(),bq.pop();
	}
	if(k) return res=-1;
	while(m)
	{
		int minn=0x3f3f3f3f;
		m--;
		if(!nq.empty()) minn=min(minn,nq.top().first);
		if(!aq.empty()) minn=min(minn,aq.top().first);
		if(!bq.empty()) minn=min(minn,bq.top().first);
		if(!abq.empty()) minn=min(minn,abq.top().first);
		if(!abqsol.empty() && !aq.empty() && !bq.empty())
		{
			int exchangecost=aq.top().first+bq.top().first-abqsol.back().first;
			if(exchangecost<=minn)
			{
				abq.push(abqsol.back()),abqsol.pop_back();
				sol.push_back(aq.top()),sol.push_back(bq.top());
				aq.pop(),bq.pop();res+=exchangecost;continue;
			}
		}
		res+=minn;
		if(!nq.empty() && minn==nq.top().first) sol.push_back(nq.top()),nq.pop();
		else if(!aq.empty() && minn==aq.top().first) sol.push_back(aq.top()),aq.pop();
		else if(!bq.empty() && minn==bq.top().first) sol.push_back(bq.top()),bq.pop();
		else if(!abq.empty() && minn==abq.top().first) sol.push_back(abq.top()),abq.pop();
	}
	return res;
}
int main()
{
	n=read(),m=read(),k=read();
	t.resize(n+1),a.resize(n+1),b.resize(n+1);
	for(int i=1;i<=n;i++)
	{
		t[i]=read(),a[i]=read(),b[i]=read();
		if(a[i] && b[i]) abq.push(make_pair(t[i],i));
		else if(!a[i] && b[i]) bq.push(make_pair(t[i],i));
		else if(a[i] && !b[i]) aq.push(make_pair(t[i],i));
		else if(!a[i] && !b[i]) nq.push(make_pair(t[i],i));
	} 
	int tottime=solve();
	printf("%d\n",tottime);
	if(tottime==-1) return 0;
	else
	{
		for(int i=0;i<sol.size();i++) cout<<sol[i].second<<" ";
		for(int i=0;i<abqsol.size();i++) cout<<abqsol[i].second<<" ";
	}
	return 0;
}

\(2.Codeforces\ \ CF1375E\) 解題報告

\(2\ \sec\ |\ 256\ MegaBytes\)

題面概要

給定數組 \(a(n)\)

對於全部的逆序對 \((a_{l1},a_{r1}),(a_{l2},a_{r2}).....\),找出這些逆序對的一個排列並交換數對,要求操做完以後整個數列單調不降,給出一種可行方案。

解題過程

簡單想一下逆排列,你會發現這道題他很相似於冒泡排序,只不過冒泡排序是相鄰兩個數交換。

這道題怎麼轉化爲相鄰兩個數交換呢?逆排列能夠作到。

最終代碼

#include <bits/stdc++.h>
using namespace std;
const int M=1005;
int n,m,k,a[M],p[M],x[M*M],y[M*M];
int read()
{
	int x=0,f=1;
	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;
}
struct node
{
	int a,b;
	bool operator < (const node &r) const
	{
		if(a==r.a) return b<r.b;
		return a<r.a;
	}
} s[M];
signed main()
{
	n=read();
	for(int i=1; i<=n; i++) s[i].a=read(),s[i].b=i;
	sort(s+1,s+1+n);
	for(int i=1; i<=n; i++) a[s[i].b]=i,p[i]=s[i].b;
	for(int i=n; i>=1; i--)
	{
		for(int j=a[i]+1; j<=i; j++) x[++k]=p[j],y[k]=i;
		for(int j=a[i]; j<i; j++) p[j]=p[j+1],a[p[j]]=j;
	}
	printf("%d\n",k);
	for(int i=1; i<=k; i++) printf("%d %d\n",x[i],y[i]);
	return 0;
}

\(3.Codeforces\ \ CF1361C\) 解題報告

\(3\ \sec\ |\ 512\ MegaBytes\)

題面概要

給定 \(n\) 對珠子,每對珠子內部已經連好了,如今你再來連 \(n\) 條邊,每條邊的權值爲知足 \(2^k\ |\ u\bigoplus v\),最後全部珠子要造成一個環,問最小的邊權最大是多少。

\(1\le n\le 5\times 10^5,\ 0\le k\le 20\)

解題過程

首先觀察出一個性質:一個數 \(x\) 能被最大的 \(2^k\) 整除的 \(k\) 是多少?是 \(2^{lowbit(x)}\)​。​

這樣一來,咱們就能夠枚舉一下答案,而後看一眼每兩個數異或起來的 \(lowbit\) 是多少。此時時間複雜度是 \(\Theta(20\times n^2)\),過不去。

既然這是一個圖論題,咱們考慮連邊。若是兩個數的 \(lowbit=x\),那麼咱們就把這兩個數都向 \(2^x-1\) 這個虛擬節點連一下邊,最後在整個圖上跑一次歐拉回路便可。

最終代碼

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5;
const int M=1<<20;
int n,a[N],b[N],deg[M];
int head[N+M+10],tot,sta[N<<2],top;
bool vis[N<<2];
int read()
{
	int x=0,f=1;
	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;
}
struct EDGE
{
	int nxt,to;
} edge[N<<2];
inline void add_edge(int u,int v)
{
	edge[++tot].nxt=head[u];
	edge[tot].to=v;
	head[u]=tot;
}
void dfs(int u)
{
	for(int& i=head[u]; i; i=edge[i].nxt) if(!vis[i]) {int v=edge[i].to;vis[i]=vis[i^1]=1,dfs(v),sta[++top]=v;}
}
int main()
{
	n=read();
	for(int i=1; i<=n; ++i) a[i]=read(),b[i]=read();
	for(int i=20; i>=1; --i)
	{
		int S=(1<<i)-1;
		memset(deg,0,sizeof(deg));
		for(int j=1; j<=n; ++j) deg[a[j]&S]++,deg[b[j]&S]++;
		bool flag=false;
		for(int j=0; j<=S; ++j) if(deg[j]&1) {flag=true;break;}
		if(flag) continue;
		memset(vis,0,sizeof(vis));
		memset(head,0,sizeof(head));
		tot=1;
		for(int j=1; j<=n; ++j) add_edge(j,(a[j]&S)+n+1),add_edge((a[j]&S)+n+1,j),add_edge(j,(b[j]&S)+n+1),add_edge((b[j]&S)+n+1,j);
		top=0,dfs(1);
		int cnt=0;
		for(int j=1; j<=top; ++j) cnt+=(sta[j]<=n);
		if(cnt!=n) continue;
		cout<<i<<endl;
		for(int j=1; j<=top; ++j)
		{
			if(sta[j]<=n)
			{
				if(!(j<top && sta[j+1]>n)) exit(0);
				int s=sta[j+1]-n-1,id=sta[j];
				if(!((a[id]&S)==s || (b[id]&S)==s)) exit(0);
				if((a[id]&S)==s) cout<<id*2<<" "<<id*2-1<<" ";
				else cout<<id*2-1<<" "<<id*2<<" ";
			}
		}
		return 0;
	}
	cout<<0<<endl;for(int i=1; i<=n*2; ++i) cout<<i<<" ";
	return 0;
}

\(4. Codeforces\ \ CF1381C\) 解題報告

\(1\ \sec\ |\ 256\ MegaBytes\)

題面概要

給出一個長度爲 \(n\) 的序列 \(A\),請你構造一個一樣長度的序列 \(B\),知足:

\(i.\) 剛好有 \(y\) 個數字與 \(A\) 相同。

\(ii.\) 再這 \(y\) 個數字中,剛好有 \(x\) 個與 \(A\) 中的數字相同,位置也相同。

\(1\le n\le 1\times10^5,\ 1\le a_i\le n+1\)

解題過程

觀察到這個 \(n+1\) 很是有趣,說明至少有一個數字 \(A\) 中沒有出現過,能夠用來填補那 \(n-y\) 個空位。

想一下,有 \(y-x\) 個數字不能與 \(A\) 中位置相同,這說明咱們要貪心地努力下降這 \(y-x\) 個數字的打亂難度,即,讓出現次數最多的數字的出現次數儘可能少。因此,對於那 \(x\) 個數,咱們每次選擇當前出現次數最多的數放上去。剩下的 \(y-x\) 個,直接找機會放便可。

最終代碼

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int T,n,x,y;
vector<int> apptimes[N];
vector<int> ans;
vector<pair<int,int> > res;
priority_queue<pair<int,int>,vector<pair<int,int> >,less<pair<int,int> > > q;
int read()
{
	int x=0,f=1;
	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;
}
int main()
{
	T=read();
	while(T--)
	{
		res.clear();
		while(!q.empty()) q.pop();
		n=read(),x=read(),y=read()-x;
		for(int i=1;i<=n+1;i++) apptimes[i].clear();
		ans.resize(n+2);
		ans.clear();
		for(int i=1;i<=n;i++) apptimes[read()].push_back(i);
		int avail=-1;
		for(int i=1;i<=n+1;i++) 
		{
			if(!apptimes[i].size()) avail=i;
			else q.push(make_pair(apptimes[i].size(),i));
		}
		int tmp=x;
		while(tmp--)
		{
			pair<int,int> p=q.top();q.pop();
			ans[apptimes[p.second].back()]=p.second;
			apptimes[p.second].pop_back();
			if(apptimes[p.second].size()) q.push(make_pair(apptimes[p.second].size(),p.second));
		}
		int maxn=0;
		for(int i=1;i<=n+1;i++)
		{
			maxn=max(maxn,(int)apptimes[i].size());
			for(int j=0;j<(int)apptimes[i].size();j++) res.push_back(make_pair(apptimes[i][j],i));
		}
		if(n-x-y<(maxn<<1)-n+x) {puts("NO");continue;}
		puts("YES");
		int cnt=0;
		for(int i=0;i<res.size();i++)
		{
			maxn%=res.size();if(cnt==y) break;
			if(res[i].second!=res[maxn].second) ans[res[i].first]=(res[maxn++].second),cnt++;
		}
		for(int i=1;i<=n;i++) cout<< (ans[i]?ans[i]:avail) <<" ";
		puts("");
	}
	return 0;
}
相關文章
相關標籤/搜索