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\)。
然后我们让:
- \(a_i\ge x\) 时,\(t_i=1\)。
- \(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;
}

浙公网安备 33010602011771号