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

Codeforces Round #733 (Div. 1 + Div. 2)

Codeforces Round #733 (Div. 1 + Div. 2)

A - Binary Decimal

定義好數為各位是\(0\)或者\(1\)的十進位制數。

給出數\(n\),求出最少的好數的數量,使得這些好數相加恰好為\(n\)

對於\(100\%\)的資料滿足\(1 \leq n \leq 10^9\)

注意到,十進位制數的減法是按位減去,增加一個好數,意味著將\(n\)各位上減去這個好數對應位子上的\(0\)或者\(1\)

最終情況是\(n\)各個位置上的數都為\(0\),因此最少的好數的數量等於\(n\)各個數位上的數值的最大值。

時間複雜度\(O(lg n)\)

# include <bits/stdc++.h>
using namespace std;
int a[10];
int main() {
	int t; scanf("%d",&t);
	while (t--) {
			int n; scanf("%d",&n); int ans=0;
		while (n) {
			ans=max(ans,n%10);
			n/=10;
		}
		printf("%d\n",ans);
	}
	return 0;
}

B - Putting Plates

\(n\times m\)的桌子,只有邊緣處才能放盤子\(1\),否則是\(0\)

且兩個盤子不能相鄰擺放,求出放最多盤子時的方案。

對於\(100\%\)的資料滿足\(3 \leq n,m \leq 20\)

\(n,m\)奇偶性情況討論即可。

時間複雜度\(O(nm)\)

# include <bits/stdc++.h>
using namespace std;
int a[25][25];
int main()
{
	int t; scanf("%d",&t);
	while (t--) {
		int n,m;
		scanf("%d%d",&n,&m);
		for (int i=1;i<=n;i++)
			for (int j=1;j<=m;j++)
				a[i][j]=0;
		if ((n&1)&&(m&1)) {
			for (int i=1;i<=m;i+=2) a[1][i]=1,a[n][i]=1;
			for (int i=1;i<=n;i+=2) a[i][1]=1,a[i][m]=1;
		} else if (n%2==0&&m%2==0) {
			for (int i=2;i<=m-1;i+=2) a[1][i]=1;
			for (int i=m-1;i>=2;i-=2) a[n][i]=1;
			for (int i=2;i<=n-1;i+=2) a[i][m]=1;
			for (int i=n-1;i>=2;i-=2) a[i][1]=1;
		} else if (n%2==1&&m%2==0) {
			for (int i=2;i<=m;i+=2) a[1][i]=1;
			for (int i=1;i<=m-1;i+=2) a[n][i]=1;
			for (int i=n;i>=2;i-=2) a[i][1]=1;
			for (int i=1;i<=n-1;i+=2) a[i][m]=1;
		} else {
			for (int i=1;i<=m-1;i+=2) a[1][i]=1;
			for (int i=m;i>=2;i-=2) a[n][i]=1;
			for (int i=1;i<=n;i+=2) a[i][1]=1;
			for (int i=2;i<=n;i+=2) a[i][m]=1;
		}
		for (int i=1;i<=n;i++) {
			for (int j=1;j<=m;j++)
				printf("%d",a[i][j]);
			puts("");
		}	
	}
	return 0;
}

C - Pursuit

一場比賽由若干輪組成,每輪會有一個得分\(w\in[0,100]\)

\(k\)輪比賽後的得分為之前輪次的最高的\(k-\lfloor \frac{k}{4} \rfloor\)個分數之和。

\(A,B\)進行了\(n\)輪比賽,每輪的得分分別為\(a_i,b_i\)

詢問至少再過多少輪比賽\(A\)得分有可能大於等於\(B\)的得分。

對於\(100\%\)的資料\(1 \leq n\leq 10^5\)

本題答案具有單調性,先二分這個答案。

check答案是否合法的時候可以貪心,\(A\)在後面的比賽中全部獲得\(100\)分,\(B\)在後面的比賽中全部獲得\(0\)分。

時間複雜度為\(O(n log_2^2 n)\)

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=3e5+10;
int a[N],aa[N],b[N],bb[N],n;
bool check(int x) {
	for (int i=1;i<=n;i++) {
		a[i]=aa[i]; b[i]=bb[i];
	}
	for (int i=n+1;i<=n+x;i++) a[i]=100; 
	for (int i=n+1;i<=n+x;i++) b[i]=0; 
	sort(a+1,a+1+n+x); reverse(a+1,a+1+n+x);
	sort(b+1,b+1+n+x); reverse(b+1,b+1+n+x);
	x=(n+x)-(n+x)/4;
	int res1=0; for (int i=1;i<=x;i++) res1+=a[i];
	int res2=0; for (int i=1;i<=x;i++) res2+=b[i];
	return res1>=res2;
}
signed main()
{
	int t; scanf("%lld",&t);
	while (t--) {
		scanf("%lld",&n);
		for (int i=1;i<=n;i++) scanf("%lld",&aa[i]);
		for (int i=1;i<=n;i++) scanf("%lld",&bb[i]);
		int l=0,r=2*n,ans;
		while (l<=r) {
			int mid=(l+r)/2;
			if (check(mid)) ans=mid,r=mid-1;
			else l=mid+1;
		}
		printf("%lld\n",ans);	
	}
	return 0;
}

D - Secret Santa

給出\(n\)個數字\(a_i\),且保證\(a_i\ne i\)

求一個排列\(b_i\)滿足\(b_i\ne i\)\(a_i = b_i\)的值儘量多。

對於\(100\%\)的資料滿足\(2 \leq n \leq 2\times 10^5\)

可以證明最終\(a_i = b_i\)儘量多的最終值為\(a_i\)中不同元素個數。

必要性顯然,下面給出一個構造方案說明充分性:

首先對於每一個\(i\),建一條$i \(到\)a_i$的有向邊。然後對有有多個進入該點的邊任意保留一條。

將這些邊上的點,設為已經加入圖中。

下面對於未加入圖中的點\(i\)處理。

找到\(a_i\),並求出\(a_i\)為目標的唯一一條邊連線的點\(u\),保留\(i\)\(a_i\)的邊,把\(u\)\(a_i\)的邊改為\(u\)\(i\)的邊。

這樣,增加一個滿足\(a_i=b_i\),又減少一個滿足\(a_i = b_i\)的點。

由於按照這樣構造,每個點\(i\)有且只有一個出邊,\(b_i\)構造完成。

時間複雜度為\(O(n)\)

# include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
map<int,int>mp;
vector<int>in[N];
int a[N],ans[N];
int main() {
	int t; scanf("%d",&t);
	while (t--) {
		int n; scanf("%d",&n);
		mp.clear();
		for (int i=1;i<=n;i++) in[i].clear(),ans[i]=0;
		for (int i=1;i<=n;i++) {
			scanf("%d",&a[i]);
			mp[a[i]]=1; in[a[i]].push_back(i);
		}
		for (int i=1;i<=n;i++) if (in[i].size()) {
			ans[in[i][0]]=i;
		}
		for (int i=1;i<=n;i++) if (in[i].size()==0) {
			int u=i,v=i; while (ans[v]) v=ans[v];
			if (u==v) continue;
			ans[v]=u;
		}
		for (int i=1;i<=n;i++) if (!ans[i]) {
			ans[i]=a[i]; ans[in[a[i]][0]]=i; in[a[i]][0]=i;
		}
		printf("%d\n",mp.size());
		for (int i=1;i<=n;i++) printf("%d ",ans[i]);
		puts("");
	}
	return 0;
}

E - Minimax

一個字串\(s\),每個位置\(i\)有函式\(f(i)\),其值其字首和字尾(長度不能是\(i\))重合的最大距離。

\(g(s) = min_{i=1}^{len(s)} f(i)\)

重新排列\(s\)中字母時,要求字典序最小的構造\(s\)字串的方案,使得\(g(s)\)的值最小。

對於\(100\%\)的資料\(1 \leq |s|\leq 10^5\)

分類討論:

  1. 當字母數只有\(1\)種時,輸出原字串即可。
  2. 當存在一個字元只出現一次時,將這種字元中字典序最小的放在首位,後面排序即可,答案為\(0\)
  3. 當所有字元出現都大於等於\(2\)次,
    • 當字母數只有\(2\)種時,不妨設字典序較小的字元為\(a\),字典序較大的字元為\(b\)
      • \(a\)的數目足夠少,答案為\(1\),\(aabababab...abbbb\)
      • \(a\)的數目不多也不少的時候,答案為\(1\),\(aababab...aba\)
      • \(a\)的數目足夠多,答案為\(1\),\(abbb...bbbaa...aa\)
    • 當字母數大於等於\(3\)時,
      • \(a\)的數目足夠少時,答案為\(1\), \(aabababacacadddeeefff...\)
      • \(a\)的數目足夠多時,答案為1,\(abaaa...aaacbbb...bccc...cddd...\)

時間複雜度為\(O(|s|)\)

# include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
char s[N];
int c[27];
void solve(){
	int cnt=0,l=0;
	for (int i=0;i<26;i++) if (c[i]==1) {
		putchar(i+'a'); c[i]--;
		for (int j=0;j<26;j++)
			for (int k=1;k<=c[j];k++)
				putchar(j+'a');
		return;		
	} else if (c[i]>1) cnt++,l+=c[i];
	if (cnt==1) {
		printf("%s",s);
	}else if (cnt==2) {
		
		int p1=-1,p2=-1;
		for (int i=0;i<26;i++) if (c[i]) {
			if (p1==-1) p1=i; else p2=i;
		}
		if (c[p1]<=(l+1)/2) {
			putchar(p1+'a');
			for (int i=2;i<=c[p1];i++) putchar(p1+'a'),putchar(p2+'a');
			for (int i=c[p1];i<=c[p2];i++) putchar(p2+'a');
		} else if (c[p1]<=l/2+1){
			putchar(p1+'a');
			for (int i=3;i<=c[p1];i++) putchar(p1+'a'),putchar(p2+'a');
			putchar(p1+'a');
		} else {
			putchar(p1+'a');
			for (int i=1;i<=c[p2];i++) putchar(p2+'a');
			for (int i=2;i<=c[p1];i++) putchar(p1+'a');
		}
	} else {
		int ps=-1;
		for (int i=0;i<26;i++) if (c[i]) {
			ps=i; break;
		}
		if (c[ps]<=(l+1)/2) {
			vector<char>tmp; tmp.clear();
			for (int i=0;i<26;i++) if (i!=ps) {
				for (int j=1;j<=c[i];j++) tmp.push_back(i+'a');
			}
			putchar(ps+'a');
			int res=c[ps]-1;
			for (int i=0;i<tmp.size();i++) {
				if (res) putchar(ps+'a');
				putchar(tmp[i]);
				if (res) res--;
			}
		} else if (c[ps]-1<=l/2) {
			vector<char>tmp; tmp.clear();
			for (int i=0;i<26;i++) if (i!=ps) {
				for (int j=1;j<=c[i];j++) tmp.push_back(i+'a');
			}
			putchar(ps+'a');
			int res=c[ps]-1;
			for (int i=0;i<tmp.size();i++) {
				if (res) putchar(ps+'a');
				putchar(tmp[i]);
				if (res) res--;
			}
			putchar(ps+'a');	
		} else {
			int p1=-1,p2=-1,p3=-1;
			for (int i=0;i<26;i++) if (c[i]) {
				if (p1==-1) p1=i;
				else if (p2==-1) p2=i;
				else if (p3==-1) p3=i;
			}
			putchar(p1+'a'); putchar(p2+'a');
			for (int i=2;i<=c[p1];i++) putchar(p1+'a');
			putchar(p3+'a');
			for (int i=2;i<=c[p2];i++) putchar(p2+'a');
			for (int i=2;i<=c[p3];i++) putchar(p3+'a');
			for (int i=0;i<26;i++) if (i>p3) {
				for (int j=1;j<=c[i];j++) putchar(i+'a'); 
			}
		}
	}
}
int main() {
	int t; scanf("%d",&t);
	while (t--) {
		scanf("%s",s); int l=strlen(s);
		for (int i=0;i<26;i++) c[i]=0;
		for (int i=0;i<l;i++) c[s[i]-'a']++;
		solve();
		puts("");	
	}
	return 0;
}