1. 程式人生 > 其它 >Codeforces Round #699 (Div. 2)

Codeforces Round #699 (Div. 2)

E.Sorting Books

題目描述

點此看題

解法

\(\tt Almost\space art!The\space art\space of\space enumeration!\)

不難發現每本書最多移動一次,移動多次一定是不優的。

那麼每本書就有兩種狀態:不移動和移動。我們列舉每本書的狀態,如果以最後一本不動書為界限,那麼前面的書如果屬於同一種類,那麼一定同時移動或者同時不移動,否則這本不動書就會使他們不能相聚。

所以我們列舉最後一本不動書,它後面的數一定要動,現在要決策它前面的書,其實就是選出來書種類的區間不能相交,那麼設 \(dp[i]\) 表示前 \(i\) 本書最大的不動書個數,預處理出每種書的左端點 \(l[i]\)

,右端點 \(r[i]\) 和出現次數 \(cnt[i]\) 就可以簡單轉移了。

但是程式碼中你會發現名與實的分離,去看註釋!

總結

當沒有思路的時候,列舉法會有奇效,雖然我們不用他實現程式碼,但是列舉有助於思考。

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M = 500005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,a[M],l[M],r[M],dp[M],cnt[M];
vector<pair<int,int>> g[M]; 
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		if(!l[a[i]]) l[a[i]]=i;
		r[a[i]]=i;
		cnt[a[i]]++;
	}
	for(int i=1;i<=n;i++)
		if(cnt[i])
			g[l[i]-1].push_back(make_pair(r[i],cnt[i]));
	for(int i=0;i<n;i++)
	{
		for(auto x:g[i])
			dp[x.first]=max(dp[x.first],dp[i]+x.second);
		dp[i+1]=max(dp[i+1],dp[i]);
	}
	int ans=n;
	for(int i=1;i<=n;i++) cnt[i]=0;
	for(int i=n;i>=1;i--)
	{
		cnt[a[i]]++;
		ans=min(ans,n-dp[i-1]-cnt[a[i]]);
		//後面的書不應該都動嗎?為什麼要減cnt[a[i]]
		//因為dp[i-1]其實沒有算清楚
		//對於a[i]這個種類的書,可能會不動 
		//但是我們在前面考慮不到其不動的情況(看轉移)
		//所以實際上是考慮前面不動的情況 
	}
	printf("%d\n",ans);
}

F. AB Tree

題目描述

點此看題

解法

顯然的思路是按深度分層,最理想的狀況是每一行的字元都一樣,答案是深度。

如果不能達到這個狀態呢?那麼構造給出最優解是深度\(+1\)

我們按層數從小到大考慮,如果某一層不能填入相同的字元,設出現次數較大的字元為 \(c\),因為要降低對兒子的影響,所以把非葉節點填入顏色 \(c\),設 \(m\) 為未填的字元,因為非葉節點的出現次數 \(\leq\frac{m}{2}\)\(c\geq\frac{m}{2}\),所以一定能填滿。然後把 \(c\) 填入這一層的葉節點,剩下的就只有另一種顏色的,填入到其它點中,不難發現只有當前層會多一種不同的字元。

現在的問題是判斷能不能每行填入相同的字元,這是個揹包問題,物品就是每一層的節點數,但是直接做是 \(O(n^2)\) 的,優化揹包需要觀察一下題目性質。因為物品的種類數最多是 \(\sqrt n\) 的,而我們求解的是存在性問題,多重揹包是可以 \(O(1)\) 跑的,只要記錄這一種物品還能取多少,就可以類似完全揹包跑了,時間複雜度 \(O(n\sqrt n)\)

總結

我總結不動了,構造法的使用總是出其不意。

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 100005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,x,y,tot,f[M],d[M],son[M];
int a[M],b[M],cnt[M],dp[500][M];
vector<int> v[M];char ans[M];
struct edge
{
	int v,next;
}e[2*M];
void dfs(int u,int fa)
{
	son[u]=0;
	d[u]=d[fa]+1;
	v[d[u]].push_back(u);
	m=max(m,d[u]);
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==fa) continue;
		dfs(v,u);son[u]=1;
	}
}
void work(int k,int n)
{
	if(!k) return ;
	int sl=a[b[k]]-dp[k][n];
	cnt[b[k]]=sl;
	work(k-1,n-sl*b[k]);
}
signed main()
{
	n=read();x=read();y=n-x;
	for(int i=2;i<=n;i++)
	{
		int j=read();
		e[++tot]=edge{i,f[j]},f[j]=tot;
		e[++tot]=edge{j,f[i]},f[i]=tot;
	}
	dfs(1,0);
	for(int i=1;i<=m;i++)
		a[v[i].size()]++;
	memset(dp,-1,sizeof dp);
	dp[0][0]=0;
	for(int i=1;i<=n;i++)
	{
		if(!a[i]) continue;
		b[++k]=i;
		for(int j=0;j<=n;j++)
		{
			if(dp[k-1][j]>=0) dp[k][j]=a[i];
			if(j>=i) dp[k][j]=max(dp[k][j],dp[k][j-i]-1);
		}
	}
	if(dp[k][x]>=0)
	{
		work(k,x);
		for(int i=1;i<=n;i++) ans[i]='b';
		for(int i=1;i<=m;i++)
		{
			int len=v[i].size();
			if(cnt[len])
			{
				cnt[len]--;
				for(auto x:v[i]) ans[x]='a';
			}
		}
		printf("%d\n",m);
		printf("%s\n",ans+1);
		return 0;
	}
	printf("%d\n",m+1);
	for(int i=1;i<=m;i++)
	{
		int len=v[i].size();
		if(len<=x)
		{
			for(auto j:v[i]) ans[j]='a';
			x-=len;
		}
		else if(len<=y)
		{
			for(auto j:v[i]) ans[j]='b';
			y-=len;
		}
		else if(x>y)
		{
			for(auto j:v[i])
				if(son[j]) ans[j]='a',x--;
			for(auto j:v[i])
				if(!ans[j] && x) ans[j]='a',x--;
			for(auto j:v[i])
				if(!ans[j]) ans[j]='b';
		}
		else
		{
			for(auto j:v[i])
				if(son[j]) ans[j]='b',y--;
			for(auto j:v[i])
				if(!ans[j] && y) ans[j]='b',y--;
			for(auto j:v[i])
				if(!ans[j]) ans[j]='a';
		}
	}
	printf("%s",ans+1);
}