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;
}