7.15小测

网址

(位运算)T1 求和

题目大意:

给出 \(n\) 个整数,每对数字 \((a_i,a_j)\) 之间有一个和谐度:\((a_ᵢ \operatorname{and} a_j) + (a_ᵢ \operatorname{or} a_j) + (a_ᵢ \operatorname{xor} a_j)\)
求所有不同数对的和谐度之和(每对只算一次)。
\(n\le10^6,a_i\le30000\)

题解:

看到二进制,第一时间想拆位,先考虑 \(\operatorname{and}\) 运算。

例子:\(1,2,3\)

\(n\) 个数纵向排列,然后分解为 \(2\) 进制,从最低位(也就是最右边)开始往高位一位一位截取,\(1,2,3\) 对应 01,10,11 最低位分别为1,0,1。我们知道 \(\operatorname{and}\) 需要 \(2\) 个都为 \(1\) 才为 \(1\),所以这里 \(3\) 个数有 \(2\) 个为 \(1\),所以所有的方法数为 \(C_2^2\) 只有一种,此时为第一位,二进制值为 \(2^0\),所以最低位的和为 \(1\times1=1\)

我们再截取第二位,分别为 \(0,1,1\) 这时候也有 \(2\)\(1\),那么所有组合方法有 \(C_2^2\) 种,但此时是第二位,二进制值为 \(2^1\),所以第二位 \(\operatorname{and}\) 和为\(1\times2=2\)

所以此时这 \(3\) 个数所有对数的 \(\operatorname{and}\) 和谐数为 \(1+2=3\)

\(\operatorname{or}\)\(\operatorname{xor}\) 道理一样,分别从 \(0\)\(1\) 中筛选数进行组合。

代码

#include<bits/stdc++.h>
#define endl '\n'
#define int long long

using namespace std;
const int N=1e6+10;
int n,a[N],And,Or,Xor,pos=1;

signed main(){
	cin.tie(nullptr)->sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=63;i++){
		int bit=0;
		for(int j=1;j<=n;j++){
			if(a[j]&1) bit++;
			a[j]>>=1;
		}
		int k=bit*(bit-1)/2;
		And+=k*pos;
		Or+=(bit*(n-bit)+k)*pos;
		Xor+=bit*(n-bit)*pos;
		pos<<=1;
	}
	cout<<And+Or+Xor<<endl;
	return 0;
}

T2 头牛

题目描述:

约翰有 \(n(1\le n\le10^5)\) 头牛,牛的品种为 \(G\)\(H\)。奶牛站成一排,编号为 \(1\sim n\)
\(i\) 头牛有自己的列表:该列表包含从第 \(i\) 只奶牛到第 \(e_i(i\le e_i\le n)\) 只奶牛。
每种牛都有一只领导者,领导者一定符合以下两条规则之一:

  • 领导者的列表包含另一种品种的领导者。
  • 领导者的列表包含了所有和自己同品种的牛。
    求可能成为领导者的牛对数(每种品种各选一个领导者)。

题解:

首先我们找出第一种牛(假设为 G 品种)的最后一个牛的位置 \(r\),在找出第二种牛(假设为 H 品种)的第一个牛的位置 \(l\)
找出领导者对的判断条件为 \(i=1\wedge e_i\ge r\vee e_i\ge l\),解释如下:

  • \(i=1\wedge e_i\ge r\):如果当前奶牛是第一头奶牛,并且它的列表结束位置 \(e_i\ge r\)(即包含所有 G 品种奶牛),则它可以是一个领导者。这是因为如果第一头奶牛是 G,它需要知道所有 G 品种奶牛的信息。
  • $e_i\ge l:对于其他奶牛(不是第一头奶牛),如果他们的名单结束位置 \(e_i\ge l\)(即包含第一个 H 品种奶牛的位置),则它们也可以是一个领导者。这是因为这些奶牛需要知道至少一个 H 品种奶牛的信息。

两种满足一条即可。

代码:

#include<bits/stdc++.h>
#define endl '\n'
#define pi pair<int,int>

using namespace std;
const int N=1e5+10;
string s;
int n,l,r,a[N],ans;

signed main(){
	cin.tie(nullptr)->sync_with_stdio(false);
	cin>>n>>s;
	s="#"+s;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<=n;i++)
		if(s[i]==s[1])
			r=i;
	for(int i=2;i<=n;i++)
		if(s[i]!=s[1]){
			l=i;
			break;
		}
	for(int i=1;i<l;i++)
		if((i==1&&a[i]>=r)||a[i]>=l)
			ans++;
	cout<<ans<<endl;
	return 0;
}

(二分)*T3 K-th Number

题目描述:

给定一个包含 \(n(1\le n\le10^5)\) 个元素的数组 \(a(1\le a_i\le10^9)\),请按照如下规则构建一个初始为空的数组 \(b\)

对于每个数组 \(a\) 中长度大于等于 \(k(1\le k\le n)\) 的区间,找到该区间的第 \(k\) 大元素,并将这个元素添加到数组 \(b\) 中。

求输出数组 \(b\) 中的第 \(m(1\le m<2^{32})\) 大元素。

题解:

我们不妨设 \(x\ge ans\),看看满足这个条件的情况是什么。其实就是 \(B\) 数组大于等于的 \(x\) 的至少有 \(m\) 个。
现在我们就要求 \(b\) 数组大于等于 \(x\) 的数有多少个,即有多少个区间的第 \(k\) 大大于等于 \(x\)
然后我们让:

  1. \(a_i\ge x\) 时,\(t_i=1\)
  2. \(a_i<x\) 时,\(t_i=0\)

\(s_i\)\(t_i\) 的前缀和。
当区间 \([i,j]\) 的和大于等于 \(k\) 时,说明这个区间有至少 \(k\) 个大于等于 \(x\) 的数,那么这个区间的第 \(k\) 大一定大于等于 \(x\)
因此,假设 \(i\) 为右端点,那么对于所有 \(s_i-s_{j-1}\gek\)\(j\) 区间 \([i,j]\) 都能满足条件。
一开始想的是对于每个 \(i\),用树状数组求 \(j\) 有多少个,属实智障。
在想想,当某个 \(j\) 满足时,其所对应的最大 \(j\) 一定是单调不减的。我们从 \(1\sim n\) 扫一遍即可。
复杂度是 \(O(n\log nV)\)

话说好像不能用主席树啊。

代码:

#include<bits/stdc++.h>
#define endl '\n'
#define int long long

using namespace std;
const int N=1e5+10;
int n,k,m,a[N],s[N];

inline int read(){
	int x;
	cin>>x;
	return x;
}

inline int check(int x){
	int l=0,sum=0;
	for(int i=1;i<=n;i++) s[i]=s[i-1]+(a[i]>=x);
	for(int i=k;i<=n;i++){
		while(s[i]-s[l]>=k) l++;
		sum+=l;
	}
	return sum>=m;
}

inline void solve(){
	n=read(),k=read(),m=read();
	int l=1,r=1;
	for(int i=1;i<=n;i++) r=max(r,a[i]=read());
	while(l<r){
		int mid=l+r+1>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
	cout<<l<<endl;
	return ;
}

signed main(){
	cin.tie(nullptr)->sync_with_stdio(false);
	int _=read();
	while(_--) solve();
	return 0;
}

(dp)T4 backpack

题目描述

kkkw 共有 \(n(1\le n\le500)\) 个物品,第 \(i\) 个物品价值为 \(a_i(1\le a_i\le500)\)。kkkw 带了一些价值和为 \(m(1\le m\le 500)\) 的物品给 panda。不幸的是,背包破洞了,不见了一些物品(可能没有可能所有都丢了)。
求剩余物品总价值可能为多少?

题解

考虑dp。设 \(f_{i,j,k}\) 表示考虑前 \(i\) 个物品,被 kkkw 装在包里的物品价值为 \(j\),最后剩余总价值为 \(k\) 的方案是否存在。

考虑转移,对于第 \(i\) 个物品,价值为 \(w\)

  • 被 kkkw 装在包里,但掉了:\(f_{i,j,k}\gets f_{i-1,j-w,k}\)
  • 被 kkkw 装在包里了,没掉:\(f_{i,j,k}\gets f_{i-1,j-w,k-w}\)
  • 没有装在包里:\(f_{i,j,k}\gets f_{i-1,j,k}\)
    时间复杂度 \(\mathcal{O}(n^3)\)>

代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'

using namespace std;
const int N=510;
int n,m,a[N],b[N],f[N][N];

inline int read(){
	int x;
	cin>>x;
	return x;
}

signed main(){
	cin.tie(nullptr)->sync_with_stdio(false);
	cin>>n>>m;
	f[0][0]=1;
	for(int i=1;i<=n;i++){
		int x=read();
		for(int j=m;j>=x;j--)
			for(int k=j;k>=0;k--){
				f[j][k]|=f[j-x][k];
				if(k-x>=0) f[j][k]|=f[j-x][k-x];
			}
	}
	for(int i=0;i<=m;i++) if(f[m][i]) cout<<i<<" ";
	return 0;
}
posted @ 2025-07-15 16:54  酱云兔  阅读(43)  评论(0)    收藏  举报