cf补题记录

TypeDB Forces 2023 (Div. 1 + Div. 2, Rated, Prizes!)

E. The Harmonization of XOR
题意
将 [1,n] 中的整数分为k个集合,每个集合的异或和均为 x,问是否可行,可行则给出一种方案。
分析
1,首先我们要检查k个x的异或和是否等于1~n的异或和。
2,如果(1)成立,那么我们先找x的在二进制下的最高位p,在从1 ~ n中寻找第p位也为1的数i,由于i ^ x一定小于i,所以对于i我们一定能够找到一个y==i ^ x,并且对于不同的i,所对应的 j 也不同,将 i 与 j 放入一个集合,1 ~ n中第p位为1的个数就是我们最多可以划分的集合数tot,我们可以将奇数个集合合并,使得集合数tot-2,tot-4……tot-2*m。然后我们将剩下的元素放入一个集合,他们一定满足XOR为x,因为1 ~ n所有数的XOR满足k个x XOR。
3,所以我们先输出k-1个可行集合,再将剩下所有数放入一个集合。
代码
`
#include <bits/stdc++.h>
#define int long long
#define endl ‘\n’
#pragma GCC optimize(3)
using namespace std;
const int N = 2e5 + 5;
int n, k, x;
void solve() {
cin >> n >> k >> x;
int sum = (k % 2 == 0 ? 0 : x);
for (int i = 1; i <= n; i++)sum ^= i;
int tot = 0, p;
for (int i = 30; i >= 0; i–)
if (x & (1 << i)) {
p = i;
break;
}
for (int i = 1; i <= n; i++)
if (i & (1 << p))
tot++;
if (sum || tot < k) {
cout << “NO” << endl;
return;
}
cout << “YES” << endl;
if (k == 1) {
cout << n << " ";
for (int i = 1; i <= n; i++)cout << i << " ";
cout << endl;
} else {
int cnt = 0;
set s;
for (int i = 1; i <= n; i++)s.insert(i);
if (s.count(x)) {
cout << 1 << " " << x << endl;
cnt++;
s.erase(x);
}
for (int i = 1; i <= n; i++) {
if (!s.count(i) || !s.count(x ^ i))continue;
if (cnt == k - 1)break;
cnt++;
cout << 2 << " " << i << " " << (i ^ x) << endl;
s.erase(i);
s.erase(i ^ x);
}
cout << s.size() << " ";
for (auto i : s)cout << i << " ";
cout << endl;
}

}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T–)
solve();
return 0;
}
`


Codeforces Round #850 (Div. 2, based on VK Cup 2022 - Final Round)

E. Monsters (hard version)

题意
这里有 n(n≤2e5)个怪物,第 i 个怪物的血量为 ai (ai≤n)。你可以执行以下操作:
1,对一个怪物造成一点伤害只能使用一次。
2.令场上所有怪物的血量减一,如果此时有一个怪物的血量被减少到0了,则再让所有怪物的血量减一,一直循环下去。
对与[a1,a2,a3……ak]的每个前缀,问最少需要多少次操作1
分析
对于[4 ,1 ,5 ,4 ,1 ,1]数组,排序后为[1,1,1,4,4,5],我们执行操作二,1被干掉,然后此时4收到2点伤害,所以我们要对4使用2次操作一,然后第二个4受到3点伤害,我们要对它用一次操作一,然后伤害提升至4,再对5用一次操作一。我们发现[1,1,1,4,4,5],[1,1,4,4,5],[1,4,4,5]答案相同,因为一开始1就被干掉了,因此当我们把伤害叠到i时,伤害为i的就会被直接干掉,对于从1 ~ i 的矩阵,只要有i个块就够了,多余的可以删除并且不影响答案。pre[i]表示大小从1 ~ i的数的个数,如果pre[i]>i的话,就可以删除它。因为我们上一次保证是最优,所以加入一个元素最多只要删除一个元素,因此我们找到一个最小的i 使得pre[i]>i,我们可以维护一个线段树,他的值为pre[i]-i,然后维护区间最大值,我们可以二分线段树,找的最小的 i 使得(1,i )使得pre[i]-i>0。
代码

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#pragma GCC optimize(3)
using namespace std;
const int N=2e5+5;
const int inf=1e10;
int n,a[N];
struct segment_tree{
	struct tree{
		int l,r,val,laz;
	}tr[N<<2];
	void pushup(int x){
		tr[x].val=max(tr[x<<1].val,tr[x<<1|1].val);
	} 
	void build(int x,int l,int r){
		tr[x].l=l,tr[x].r=r,tr[x].laz=0;
		if(l==r){
			tr[x].val=-l;
			return;
		}
		int mid=l+r>>1;
		build(x<<1,l,mid);
		build(x<<1|1,mid+1,r);
		pushup(x);
	}
	void mark(int x,int k){
		tr[x].val+=k;
		tr[x].laz+=k;
	}
	void pushdown(int x){
		mark(x<<1,tr[x].laz);
		mark(x<<1|1,tr[x].laz);
		tr[x].laz=0;
	}
	void update(int x,int l,int r,int k){
		if(tr[x].l>=l&&tr[x].r<=r){
			mark(x,k);
			return;
		}
		pushdown(x);
		int mid=(tr[x].l+tr[x].r)>>1;
		if(mid>=l) update(x<<1,l,r,k);
		if(mid<r) update(x<<1|1,l,r,k);
		pushup(x);
	}
	int query(int x,int l,int r){
		if(tr[x].l>=l&&tr[x].r<=r){
			return tr[x].val;
		}
		pushdown(x);
		int mid=(tr[x].l+tr[x].r)>>1;
		int ans=-inf;
		if(l<=mid) ans=max(ans,query(x<<1,l,r));
		if(mid<r) ans=max(ans,query(x<<1|1,l,r));
		return ans;
	}
}tr;
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	tr.build(1,1,n);
	int sum=0,siz=0;
	for(int i=1;i<=n;i++){
		sum+=a[i];siz++;
		tr.update(1,a[i],n,1);	
		if(tr.query(1,1,n)>0){
			int l=1,r=n;
			while(l<r){
				int mid=l+r>>1;
				if(tr.query(1,1,mid)>0)
					r=mid;
				else 
					l=mid+1;
			}
			sum-=l,siz--;tr.update(1,l,n,-1);
		}
		cout<<sum-siz*(siz+1)/2<<" ";
	}
    cout<<endl;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int T;
	cin>>T;
	while(T--)
		solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值