題解 CF1328 D,E,F Carousel, Tree Queries, Make k Equal


比賽鏈接

注意,題目並沒有要求同一種動物必須染相同的顏色。雖然樣例的確滿足這一點。

我們斷言,需要的顏色數不會超過\(3\)。我們從\(1\)\(3\)依次嘗試,當構造成功了就直接輸出答案。

要使答案為\(1\),顯然只有一種情況,就是整個序列只有一種動物。

要使答案為\(2\)。先把序列里每一段極長的、相同動物的連續段用同一種顏色表示,把相鄰的段塗上不同的顏色(例如:上一段是\(1\),這一段就是\(2\);上一段是\(2\),這一段就是\(1\))。

此時,還需要考慮的只有\(1\)\(n\)的關系。若\(1\)\(n\)是同一種動物,或者它們現在已經是不同的顏色了,則說明當前的塗色方法就是一個合法解。

否則,我們要做一些調整,使\(1\)\(n\)顏色不同。我們檢查序列里是否存在一個同色段的長度大於\(1\)。如果存在,則我們可以把這一段劈成兩半,前一半還是原來的顏色,后一半和原來顏色相反,且從它往后的每一段顏色都和原來反轉。這樣,\(1\)\(n\)就一定不同色了。

如果找不到長度大於\(1\)的同色段。說明我們無法用\(2\)種顏色構造出合法解。即答案為\(3\)。我們直接在原來構造的基礎上令\(n\)的顏色為\(3\)即可。

時間復雜度\(O(n)\)

參考代碼:

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

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

namespace Fread{
const int MAXN=1<<20;
char buf[MAXN],*S,*T;
inline char getchar(){
	if(S==T){
		T=(S=buf)+fread(buf,1,MAXN,stdin);
		if(S==T)return EOF;
	}
	return *S++;
}
}//namespace Fread
#ifdef ONLINE_JUDGE
	#define getchar Fread::getchar
#endif
inline int read(){
	int f=1,x=0;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline ll readll(){
	ll f=1,x=0;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
/*  ------  by:duyi  ------  */ // myt天下第一
const int MAXN=2e5;
int n,a[MAXN+5],col[MAXN+5];
bool check1(){
	for(int i=1;i<=n;++i){
		int j=i%n+1;
		if(a[i]!=a[j])return 0;
	}
	puts("1");
	for(int i=1;i<=n;++i)printf("%d ",1);puts("");
	return 1;
}
bool check2(){
	col[1]=1;
	for(int i=2;i<=n;++i){
		if(a[i]!=a[i-1]){
			col[i]=3-col[i-1];
		}
		else{
			col[i]=col[i-1];
		}
	}
	if((a[n]==a[1])||(a[n]!=a[1]&&col[n]!=col[1])){
		puts("2");
		for(int i=1;i<=n;++i)printf("%d ",col[i]);puts("");
		return 1;
	}
	for(int i=1;i<n;++i)if(a[i]==a[i+1]){
		col[i+1]=3-col[i];
		for(int j=i+2;j<=n;++j){
			if(a[j]!=a[j-1]){
				col[j]=3-col[j-1];
			}
			else{
				col[j]=col[j-1];
			}
		}
		puts("2");
		for(int i=1;i<=n;++i)printf("%d ",col[i]);puts("");
		return 1;
	}
	return 0;
}
void check3(){
	col[1]=1;
	for(int i=2;i<=n;++i){
		if(a[i]!=a[i-1]){
			col[i]=3-col[i-1];
		}
		else{
			col[i]=col[i-1];
		}
	}
	col[n]=3;
	puts("3");
	for(int i=1;i<=n;++i)printf("%d ",col[i]);puts("");
}
int main() {
	int T=read();while(T--){
		n=read();
		for(int i=1;i<=n;++i)a[i]=read();
		if(check1())continue;
		if(check2())continue;
		check3();
	}
	return 0;
}

CF1328E Tree Queries

我們要找到一個合法的\(u\),使得所有\(v_1\dots v_k\)都在\(u\)到根的路徑上,或者距離路徑上某個點距離為\(1\)

這里的距離為\(1\),肯定不會說\(v_i\)是某個路徑上點的父親,因為這種情況下它自己本身也必在路徑上。所以這里的距離為\(1\),可以轉化為,要么\(v_i\)在路徑上,要么它是路徑上某個點的兒子。也就是說,\(v_i\)\(v_i\)的父親,至少要有一個點是路徑上的點。

判斷一個點\(v\)在不在\(u\)到根的路徑上,其實就是判斷\(u\)是否在\(v\)的子樹內。這個可以用dfs序\(O(1)\)判斷。

至此,我們可以\(O(k)\)地check某一個\(u\)是否滿足條件。但顯然不能暴力枚舉所有\(u\),否則單次詢問復雜度退化為\(O(nk)\)

我們發現,如果有解,則\(v_1\dots v_k\)里深度最深的點的父親,一定是一個合法的\(u\)。所以只要找到這個深度最深的點,再check它的父親即可。

單次詢問時間復雜度\(O(k)\)

參考代碼:

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

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

namespace Fread{
const int MAXN=1<<20;
char buf[MAXN],*S,*T;
inline char getchar(){
	if(S==T){
		T=(S=buf)+fread(buf,1,MAXN,stdin);
		if(S==T)return EOF;
	}
	return *S++;
}
}//namespace Fread
#ifdef ONLINE_JUDGE
	#define getchar Fread::getchar
#endif
inline int read(){
	int f=1,x=0;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline ll readll(){
	ll f=1,x=0;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
/*  ------  by:duyi  ------  */ // myt天下第一

const int MAXN=2e5;
int n,m,dfn[MAXN+5],ofn[MAXN+5],cnt,dep[MAXN+5],fa[MAXN+5];
vector<int>G[MAXN+5];
void dfs(int u){
	dfn[u]=++cnt;
	dep[u]=dep[fa[u]]+1;
	for(int i=0;i<SZ(G[u]);++i){
		int v=G[u][i];
		if(v==fa[u])continue;
		fa[v]=u;
		dfs(v);
	}
	ofn[u]=cnt;
}
bool isanc(int ann,int u){
	return dfn[u]>=dfn[ann] && dfn[u]<=ofn[ann];
}
int K,a[MAXN+5];
bool check(int u){
	for(int i=1;i<=K;++i){
		if(isanc(a[i],u)||isanc(fa[a[i]],u))continue;
		return 0;
	}
	return 1;
}
int main() {
	n=read();m=read();
	for(int i=1;i<n;++i){
		int u=read(),v=read();
		G[u].pb(v);G[v].pb(u);
	}
	dfs(1);
	while(m--){
		K=read();
		int u=0;
		for(int i=1;i<=K;++i){
			a[i]=read();
			if(!u||dep[a[i]]>dep[u])u=a[i];
		}
		if(u==1){
			puts("YES");continue;
		}
		if(check(fa[u]))puts("YES");
		else puts("NO");
	}
	return 0;
}

CF1328F Make k Equal

先把\(a_1\dots a_n\)按從小到大排序。

顯然,最終的\(k\)個相等的數一定等於\(a_1\dots a_n\)里的某個值,不可能憑空冒出一個新的值(因為這樣代價一定更大)。

我們枚舉最終的\(k\)個相等的數等於幾。也就是枚舉\(a_1\dots a_n\)里的某個值\(x\)。因為是排好序后,所以\(x\)在序列里一定是連續的一段。我們要在這一段左邊補幾個數,右邊補幾個數,使得段的長度達到\(k\)。在左邊補幾個數,根據題目規則,顯然必須要把左邊所有的數先補成\(x-1\)。同理,如果要在右邊補幾個等於\(x\)的數,必須先把右邊所有數變成\(x+1\)

如果原序列等於\(x\)的連續段為\(a_l\dots a_r\),即\(\forall i\in[l,r]\ a_i=x\),則我們的總操作次數為:

\[\left((x-1)(l-1)-\sum_{i=1}^{l-1}a_i\right)+\left(\sum_{i=r+1}^{n}a_i-(x+1)(n-r)\right)+k-(r-l+1) \]

做前綴和、后綴和,即可\(O(1)\)計算上式。

當然,如果\(r\geq k\)或者\(n-l+1\geq k\),我們可以只補左邊,或者只補右邊。這兩種情況也要考慮到。

時間復雜度\(O(n)\)

參考代碼:

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

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

namespace Fread{
const int MAXN=1<<20;
char buf[MAXN],*S,*T;
inline char getchar(){
	if(S==T){
		T=(S=buf)+fread(buf,1,MAXN,stdin);
		if(S==T)return EOF;
	}
	return *S++;
}
}//namespace Fread
#ifdef ONLINE_JUDGE
	#define getchar Fread::getchar
#endif
inline int read(){
	int f=1,x=0;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline ll readll(){
	ll f=1,x=0;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
/*  ------  by:duyi  ------  */ // myt天下第一
const int MAXN=2e5;
const ll INF=1e18;
int n,K,a[MAXN+5];
ll pre[MAXN+5],suf[MAXN+5];
int main() {
	n=read();K=read();
	for(int i=1;i<=n;++i)a[i]=read();
	sort(a+1,a+n+1);
	for(int i=1;i<=n;++i)pre[i]=pre[i-1]+a[i];
	for(int i=n;i>=1;--i)suf[i]=suf[i+1]+a[i];
	ll ans=INF;
	for(int i=1;i<=n;++i){
		int j=i;
		while(j+1<=n&&a[j+1]==a[i])++j;
		if(j-i+1>=K){ans=0;break;}
		ans=min(ans,(ll)(a[i]-1)*(i-1)-pre[i-1]+suf[j+1]-(ll)(a[i]+1)*(n-j)+K-(j-i+1));
		if(j>=K){
			ans=min(ans,(ll)(a[i]-1)*(i-1)-pre[i-1]+K-(j-i+1));
		}
		if(n-i+1>=K){
			ans=min(ans,suf[j+1]-(ll)(a[i]+1)*(n-j)+K-(j-i+1));
		}
		i=j;
	}
	cout<<ans<<endl;
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM