Codeforces Round #547 (Div. 3) 题解

A. Game 23
乘2和乘3的次数显然不会太多,暴力dfs即可。
代码:

#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
using namespace std;
 
int n,m,ans;
void dfs(int now,int cnt){
	if(now > m) return;
	if(now == m){
		ans = min(ans,cnt);
		return;
	}
	dfs(now*2,cnt+1);
	dfs(now*3,cnt+1);
}
int main(){
	ans = INT_MAX;
	cin >> n >> m;
	dfs(n,0);
	if(ans != INT_MAX) cout << ans;
	else cout << -1;
 
	return 0;
}

B. Maximal Continuous Rest
复制一倍接到后面然后找最多连续的1就好了。

#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
using namespace std;
const int N = 5e5+10;
int n,a[N];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n;
	int ans = 0;
	fir(i,1,n){
		cin >> a[i],a[i+n] = a[i];
	}
	int now = 0;
	fir(i,1,2*n){
		if(a[i]){
			now ++ ;
			ans = max(ans,now);
		}
		else now = 0;
	}
	cout << ans;

	return 0;
}

C. Polycarp Restores Permutation
前缀和和差分互为逆运算,这题只需要求一遍前缀和,然后判断一下前缀和数组的最小值是大于0还是小于0,如果大于0那么直接把整个数组加上最小值,小于0的话就加上abs(最小值)+1.然后判断数组是否是一个排列即可。

#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
#define afir(i,a,b) for(int i = a;i >= b; -- i)
#define pb push_back
using namespace std;
const int N = 5e5+10;
int n,a[N],vis[N];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n;
	int mx = INT_MAX;
	bool f = 1;
	fir(i,1,n-1){
		cin >> a[i];
		if(!a[i]) f = 0;
		a[i] += a[i-1];
		mx = min(mx,a[i]);
	}
	if(mx < 0) mx = -mx + 1;
	afir(i,n,1) a[i] = a[i-1] + abs(mx);
	fir(i,1,n){
		if(a[i] < 1 || a[i] > n || vis[a[i]]){
			f = 0;
			break;
		}
		vis[a[i]] = 1;
	}
	if(!f) cout << -1;
	else fir(i,1,n) cout << a[i] << " ";

	return 0;
}

D. Colored Boots
小型模拟题,按题意把符合的二元对分为以下几类:

  1. 正常的字母对字母
  2. a的‘?’对b的剩余字母
  3. b的‘?’对a的剩余字母
  4. a的剩余’?‘对b的剩余’?

然后用一个vector或者数组存起来最后输出即可。

#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
#define afir(i,a,b) for(int i = a;i >= b; -- i)
#define pb push_back
using namespace std;
const int N = 5e5+10;
int n;
string a,b;
vector<int> va[26],vb[26];
vector<pair<int,int> > ans;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n >> a >> b;
	a = ' ' + a;
	b = ' ' + b;
	vector<int> vec1,vec2;
	fir(i,1,n){
		if(a[i] != '?') va[a[i]-'a'].pb(i);
		else vec1.pb(i);
		if(b[i] != '?') vb[b[i]-'a'].pb(i);
		else vec2.pb(i);
	}
	fir(i,0,25){
		int len = (int)min(va[i].size(),vb[i].size());
		fir(j,0,len-1)
			ans.pb(make_pair(va[i][j],vb[i][j]));
		reverse(va[i].begin(),va[i].end());
		reverse(vb[i].begin(),vb[i].end());
		fir(j,0,len-1) va[i].pop_back(),vb[i].pop_back();
	}
	fir(i,0,25){
		while(vec2.size()){
			if(!va[i].size()) break;
			ans.pb(make_pair(va[i].back(),vec2.back()));
			va[i].pop_back();vec2.pop_back();
		}
	}
	fir(i,0,25){
		while(vec1.size()){
			if(!vb[i].size()) break;
			ans.pb(make_pair(vec1.back(),vb[i].back()));
			vb[i].pop_back();vec1.pop_back();
		}
	}
	while(vec1.size() && vec2.size()){
		ans.pb(make_pair(vec1.back(),vec2.back()));
		vec1.pop_back(),vec2.pop_back();
	}
	cout << ans.size() << "\n";
	for(auto x:ans) cout << x.first << " " << x.second << "\n";

	return 0;
}

E. Superhero Battle
对数组求一遍前缀和数组a,再用一个mx记录前缀和的最小值。如果H+mx>0,并且a[n]>=0,那么无解。否则我们只需要循环(H-mx)/sum[n]轮,并且在下一轮里当H+mx<=0的时候输出答案即可。这题更多的是细节上面的问题,这里的讲解知识大致的思路,具体实现可以看代码

#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
#define afir(i,a,b) for(int i = a;i >= b; -- i)
#define pb push_back
#define LL long long
using namespace std;
const int N = 5e5+10;
LL h,a[N];
int n;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> h >> n;
	LL mx = 1e16,id = 1;
	fir(i,1,n){
		cin >> a[i];
		a[i] += a[i-1];
		if(a[i] < mx){
			mx = a[i];
			id = i;
		}
	}
	if(h + mx > 0 && a[n] >= 0){
		cout << -1;
		return 0;
	}
	if(h+mx <= 0){
		fir(i,1,n){
			if(a[i]+h<=0){
				cout << i;
				return 0;
			}
		}
	}
	LL ned = (h+mx)/abs(a[n]);
	if((h+mx)%abs(a[n])) ned++;
	LL ans = ned*n;
	h += ned*a[n];
	fir(i,1,n){
		if(h + a[i] <= 0){
			cout << ans + i;
			return 0;
		}
	}	
	cout << ans;

	return 0;
}

F1. Same Sum Blocks (Easy)
F2. Same Sum Blocks (Hard)
无论是Easy还是Hard,n都不会很大,所以产生的区间对也最多是15002的,那么可以按照这些区间对的权值和分类,然后在每一类中找到最大的分割方式即可。
找这个最大分割方式在Easy中可以和求LIS一样O(n2)求解,整体复杂度就是O(n3)的,Hard中可以用线段树或者前缀最大值优化一层转移,变成O(n2log(n))或者是O(n2)。做题的时候没反应过来用一个前缀最大值就可以解决,看了题解才想到不用线段树那么麻烦,所以代码实现里用的是线段树的方法。
还有就是输出方案的问题,这是dp很常见的回溯的套路。具体实现看代码吧。

#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
#define afir(i,a,b) for(int i = a;i >= b; -- i)
#define pb push_back
#define LL long long
#define pii pair<int,int>
#define mpr make_pair
using namespace std;
const int N = 2e6+10;
const int M = 3e6+10;

int n,a[N],dp[M];
vector<int> all;
int getpos(int x){
	return lower_bound(all.begin(),all.end(),x) - all.begin();
}
vector<pii> vec[M];
bool cmp(pii a,pii b){
	return a.second < b.second;
}
namespace seg{ // 单点更新 区间求最值
	#define ls 2*i
	#define rs 2*i+1
	int mx[N];
	void update(int i,int l,int r,int p,int v){
		if(l == r) return void(mx[i] = v);
		int mid = l + r >> 1;
		if(p <= mid) update(ls,l,mid,p,v);
		else update(rs,mid+1,r,p,v);
		mx[i] = max(mx[ls],mx[rs]);
	}
	int ask(int i,int l,int r,int L,int R){
		if(l >= L && r <= R) return mx[i];
		int mid = l + r >> 1;
		if(R <= mid) return ask(ls,l,mid,L,R);
		else if(L > mid) return ask(rs,mid+1,r,L,R);
		else return max(ask(ls,l,mid,L,mid),ask(rs,mid+1,r,mid+1,R));
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n;
	fir(i,1,n){
		cin >> a[i];
		a[i] += a[i-1];
		fir(j,1,i) all.pb(a[i]-a[j-1]);
	}
	sort(all.begin(),all.end());
	all.erase(unique(all.begin(),all.end()),all.end());
	fir(i,1,n){
		fir(j,1,i){
			vec[getpos(a[i]-a[j-1])].pb(mpr(j,i));
		}
	}
	int tn = all.size();
	int ans = 0,id = 0;
	fir(i,0,tn-1){
		vec[i].pb(mpr(INT_MIN,INT_MIN));
		sort(vec[i].begin(),vec[i].end());
		int nn = vec[i].size()-1;
		fir(j,1,nn){
			int newval = seg::ask(1,0,1501,0,vec[i][j].first-1) + 1;
			seg::update(1,0,1501,vec[i][j].second,newval);
			if(seg::mx[1] > ans){
				ans = seg::mx[1];
				id = i;
			}
		}
		fir(j,1,nn) seg::update(1,0,1501,vec[i][j].second,0);
	}
	cout << ans << "\n";
	int nn = vec[id].size()-1,idx=1;
	fir(i,1,nn){
		int newval = seg::ask(1,0,1501,0,vec[id][i].first-1) + 1;
		seg::update(1,0,1501,vec[id][i].second,newval);
		dp[i] = newval;
		if(dp[i] == ans) idx = i;
	}
	while(ans){
		cout << vec[id][idx].first << " " << vec[id][idx].second << "\n";
		ans--;
		afir(i,idx-1,1){
			if(dp[i] == ans && vec[id][idx].first > vec[id][i].second){
				idx = i;
				break;
			}
		}
	}

	return 0;
}

G. Privatization of Roads in Treeland
看似是一道图论题实际上是一个思维题,思考一下可以发现,假设用x个公司,那么一个节点的边如果超过了x条,那么这个节点一定会觉得不公平。所以只需要二分一下公司的数量,然后判断不公平节点的个数是否超过k即可。
比较麻烦的是染色的问题,我的染色方案是子节点从父亲节点颜色+1开始染,这种方案大概是不会冲突的。(瞎搞A了)
代码:

#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
#define afir(i,a,b) for(int i = a;i >= b; -- i)
#define pb push_back
#define LL long long
#define pii pair<int,int>
#define mpr make_pair
using namespace std;
const int N = 4e5+10;
int ans,n,k,ct,head[N],to[N],nxt[N],bel[N],col[N],num[N];
void addedge(int x,int y,int i){
	nxt[++ct] = head[x];head[x] = ct;bel[ct] = i;to[ct] = y;
}
void dfs(int u,int fa,int tot,int cnt){
	for(int i = head[u];i;i = nxt[i]){
		int y = to[i],bl = bel[i];
		if(y == fa) continue;
		col[bl] = ++ cnt;
		if(cnt == tot) cnt = 0;
		dfs(y,u,tot,cnt);
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n >> k;
	fir(i,1,n-1){
		int x,y;
		cin >> x >> y;
		addedge(x,y,i);
		addedge(y,x,i);
		num[x]++;num[y]++;
	}
	int l = 1,r = n;
	while(l < r){
		int mid = l + r >> 1;
		int nu = 0;
		fir(i,1,n) if(num[i] > mid) nu++;
		if(nu <= k) r = mid;
		else l = mid+1;
	}
	cout << l << "\n";
	dfs(1,0,l,0);
	fir(i,1,n-1) cout << col[i] << " ";

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值