Codeforces Round #936 (Div. 2)B~D

1946B - Maximum Sum

        

可以想到,每次都将最大连续子序列放到该子序列的最后,也就是每一轮都能将最大连续子序列倍增一次填到数组中,最终求结果

// Problem: B. Maximum Sum
// Contest: Codeforces - Codeforces Round 936 (Div. 2)
// URL: https://codeforces.com/contest/1946/problem/B
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
#define int long long
const LL maxn = 4e05+7;
const LL N = 5e05+10;
const LL mod = 1e09+7;
const int inf = 0x3f3f3f3f;
const LL llinf = 5e18;
typedef pair<int,int>pl;
priority_queue<LL , vector<LL>, greater<LL> >mi;//小根堆
priority_queue<LL> ma;//大根堆
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
int n , m;
vector<int>a(N , 0);
void init(int n){
	for(int i = 0 ; i <= n ; i ++){
		a[i] = 0;
	}
}
void solve() 
{
	cin >> n >> m;
	int a[n];
	for(int i = 0 ; i < n ; i ++){
		cin >> a[i];
	}		
	int maxx = 0;
	int sum = 0;
	for(int i = 0 ; i < n ; i ++){
		sum += a[i];
		maxx = max(maxx , sum);
		sum = max(0LL, sum);
	}
	int ans = mod * n;
	for(int i = 0 ; i < n ; i ++)
		ans += a[i];
	ans %= mod;
	for(int i = 0 ; i < m ; i ++){
		ans += maxx;
		maxx *= 2;
		ans %= mod;
		maxx %= mod;
	}
	
	cout << ans << endl;
}            
signed main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
	cin>>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

1946C - Tree Cutting

        

题意:问树能否分成k + 1 份,每份的子树大小大于等于x。

思路:二分答案,dfs对于大于等于x的子树直接减掉即可,模拟。

// Problem: C. Tree Cutting
// Contest: Codeforces - Codeforces Round 936 (Div. 2)
// URL: https://codeforces.com/contest/1946/problem/C
// Memory Limit: 512 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
const LL maxn = 4e05+7;
const LL N = 5e05+10;
const LL mod = 1e09+7;
const int inf = 0x3f3f3f3f;
const LL llinf = 5e18;
typedef pair<int,int>pl;
priority_queue<LL , vector<LL>, greater<LL> >mi;//小根堆
priority_queue<LL> ma;//大根堆
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
int n , m;
vector<int>a(N , 0);
void init(int n){
	for(int i = 0 ; i <= n ; i ++){
		a[i] = 0;
	}
}
int cnt = 0;
struct HLD {//轻重链剖分
    int n;
    std::vector<int> siz, top, dep, parent, in, out, seq;//子树大小 所在重链的顶部节点 深度 父亲 子树DFS序的起点 子树DFS序的终点
    std::vector<std::vector<int>> adj;
    int cur = 1;
    HLD() {}
    HLD(int n) {
        init(n);
    }
    void init(int n) {
        this->n = n;
        siz.resize(n);
        top.resize(n);
        dep.resize(n);
        parent.resize(n);
        in.resize(n);
        out.resize(n);
        seq.resize(n);
        cur = 0;
        adj.assign(n, {});
    }
    void addEdge(int u, int v) {
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    void work(int root = 1) {
        top[root] = root;
        dep[root] = 0;
        parent[root] = -1;
        dfs1(root);
        dfs2(root);
    }
    void dfs1(int u) {
        if (parent[u] != -1) {
            adj[u].erase(std::find(adj[u].begin(), adj[u].end(), parent[u]));
        }
        siz[u] = 1;
        for (auto &v : adj[u]) {
            parent[v] = u;
            dep[v] = dep[u] + 1;
            dfs1(v);
            siz[u] += siz[v];
            if (siz[v] > siz[adj[u][0]]) {
                std::swap(v, adj[u][0]);
            }
        }
    }
    void dfs2(int u) {
        in[u] = ++cur;
        seq[in[u]] = u;
        for (auto v : adj[u]) {
            top[v] = v == adj[u][0] ? top[u] : v;
            dfs2(v);
        }
        out[u] = cur;
    }
    int dfs3(int u , int x){//返回切掉的大小
		int res = siz[u];
		int cut = 0;
		for(auto v : adj[u]){
			cut += dfs3(v , x);
		}
		res -= cut;
		if(res >= x){
			cnt++;
			return siz[u];
		}
		else{
			return cut;
		}
    }
};
void solve() 
{
	cin >> n >> m;
	HLD hld(n + 5);
	for(int i = 1; i < n ; i ++){
		int u , v;
		cin >> u >> v;
		hld.addEdge(u , v);
	}
	hld.work(1);
	//cout << cnt << endl;
	int l = 0 , r = n;
	while(l < r){
		int mid = (l + r + 1) / 2;
		cnt = 0;
		hld.dfs3(1 , mid);
		if(cnt > m){//分太多了,还可以更大
			l = mid;
		}
		else{
			r = mid - 1; 
		}
	}
	cout << l << endl;
}            
int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
	cin>>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

1946D - Birthday Gift 

思路:首先异或问题想到拆位去处理

其次将整个问题看成能有多少个位置放上隔板,答案数是隔板数 + 1

接下来考虑什么情况下会使得结果>x

1、比x的最高位还要高的位数,每个区间内的个数必须为偶数,如若不能满足直接输出-1,也就是说隔板的可选位置会被限制。

2、然后我们从高位到低位依次遍历。

3、若x的某一位是1,那么如果我们将最终结果这一位变成0,那么此后的所有位都不需要讨论了,这一轮的可选位置即是最大方案数。如果变成1,那么隔板可以任意放置,可选位置不变。

4、若x的某一位是0,那么如果我们也要将其最终结果为0,同样需要满足每个区间内的个数为偶数,隔板的可能位置被限制。如果我们将其变成1,那么接下来所有操作也就没有必要了,直接跳过。

// Problem: D. Birthday Gift
// Contest: Codeforces - Codeforces Round 936 (Div. 2)
// URL: https://codeforces.com/contest/1946/problem/D
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
const LL maxn = 4e05+7;
const LL N = 5e05+10;
const LL mod = 1e09+7;
const int inf = 0x3f3f3f3f;
const LL llinf = 5e18;
typedef pair<int,int>pl;
priority_queue<LL , vector<LL>, greater<LL> >mi;//小根堆
priority_queue<LL> ma;//大根堆
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
int n , m;
vector<int>a(N , 0);
void init(int n){
	for(int i = 0 ; i <= n ; i ++){
		a[i] = 0;
	}
}
void solve() 
{
	cin >> n >> m;
	vector<int>cnt(35 , 0);
	int maxx = -1;
	for(int i = 0 ; i < 32 ; i ++){
		if((m >> i) & 1){
			cnt[i] = 1;
			maxx = i;
		}
	}	
	vector<int>num[35];
	for(int i = 1 ; i <= n ; i ++)
		cin >> a[i];
	for(int i = 1 ; i <= n ; i ++){
		for(int j = 0 ; j < 32 ; j ++){
			if((a[i] >> j) & 1){
				num[j].pb(i);
			}
		}
	}
	//大于maxx的需要偶数一组
	set<pair<int,int>>st;//不可行域
	for(int i = maxx + 1 ; i < 32 ; i ++){
		if(num[i].size() & 1){
			cout << -1 << endl;
			return;
		}
		for(int j = 0 ; j < num[i].size() ; j += 2){
			st.insert({num[i][j] , num[i][j + 1] - 1});//所有的不可域
		}
	}
	vector<int>diff(n + 5 , 0);
	for(auto it : st){//不可行域的合并
		diff[it.x]++;
		diff[it.y + 1]--;
	}
	vector<int>vis(n + 5 , 0);
	int cntt = 0;
	for(int i = 0 ; i <= n ; i ++){
		cntt += diff[i];
		vis[i] = cntt;	
	}
	//如果某一位是1 , 那么任意区间都可选 ,但是需要满足后续,也就是区间往后延,如果某一位需要是0,那么就必须全偶数,也就是说不可行域是在不断增加的
	int ans = -100;
	int i;
	for(i = maxx ; i >= 0 ; i --){
		if(cnt[i] == 1){//这位是1
			//取0操作,后续不用管
			if(num[i].size() == 0){
				int c = 0;
				for(int l = 1 ; l < n ; l ++){
					c += (vis[l] == 0);
				}
				ans = max(ans , c);
			}
			else if(num[i].size() % 2 == 0){//可以取到0
				int st = 1;
				int c = 0;
				for(int j = 0 ; j < num[i].size() ; j += 2){
					int l = st , r = num[i][j] - 1;
					for(int t = l ; t <= r ; t ++){
						if(vis[t] == 0){
							c++;
						}
					}
					st = num[i][j + 1];
				}
				for(int t = st ; t < n ; t ++){
					if(vis[t] == 0){
						c ++;
					}
				}
				ans = max(ans , c);
			}
			else{
				continue;//必须取到1,那么也就是随便选
			}
		}
		else{
			//不可行域的减少
			if(num[i].size() & 1){
				if(ans < 0){
					cout << -1 << endl;
				}
				else{
					cout << ans + 1 << endl;
				}
				return;
			}
			else{//不可行域更新
				for(int j = 0 ; j < num[i].size() ; j += 2){
					int l = num[i][j] , r = num[i][j + 1] - 1;
					for(int t = l ; t <= r ; t ++)
						vis[t]++;
				}
			}
		}
	}
	int k = 0;
	for(int i = 1 ; i < n ; i ++){
		k += (vis[i] == 0);
	}
	ans = max(ans , k);
	cout << ans + 1 << endl;
}            
int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
	cin>>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值