25.2.25补题
2.25abc补题
abc391F K-th Largest Triplet
题意
给定三个长度为 n n n的整数数组 A , B , C A,B,C A,B,C,以及一个整数 K K K,对于整数 i , j , k ( 1 ≤ i , j , k ≤ n ) i,j,k (1 \leq i, j,k \leq n) i,j,k(1≤i,j,k≤n)的每一个 N 3 N^3 N3选项,计算 A i ∗ B j + A i ∗ C k + b j ∗ C k A_i*B_j+A_i*C_k+b_j*C_k Ai∗Bj+Ai∗Ck+bj∗Ck的值,并输出第 K K K大的值
思路
- 先将 A , B , C A,B,C A,B,C数组降序排列后,第一大的下标肯定是 [ 1 , 1 , 1 ] [1,1,1] [1,1,1]
- 则第二大的下标一定为 [ 2 , 1 , 1 ] , [ 1 , 2 , 2 ] , [ 1 , 1 , 2 ] [2,1,1],[1,2,2],[1,1,2] [2,1,1],[1,2,2],[1,1,2]其中之一,由此可知每个确定的最大值可以推出三个次大值,接下来的最大值就在三个次大值之中产生
- 可以使用优先队列维护每个最大值,再将其推出的三个次大值不断丢进去
- 为避免重复,使用set维护已经得到过的下标
代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define inf 0x3f3f3f3f
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
using namespace std;
const double eps=1e-6;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));
void solve()
{
int n,k;
cin>>n>>k;
vector<ll>a(n+1);
auto b=a;
auto c=a;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=n;i++) cin>>c[i];
sort(all(a),greater<ll>());
sort(all(b),greater<ll>());
sort(all(c),greater<ll>());
set<PIII>s;
priority_queue<pair<ll,PIII>>q;
q.push({a[1]*b[1]+a[1]*c[1]+b[1]*c[1],{1,1,1}});
s.insert({1,1,1});
while(k>1)
{
auto [x,y]=q.top();
q.pop();
//cout<<x<<endl;
k--;
if(y[0]<n && s.find({y[0]+1,y[1],y[2]})==s.end())
{
q.push({a[y[0]+1]*b[y[1]]+a[y[0]+1]*c[y[2]]+b[y[1]]*c[y[2]],{y[0]+1,y[1],y[2]}});
s.insert({y[0]+1,y[1],y[2]});
}
if(y[1]<n && s.find({y[0],y[1]+1,y[2]})==s.end())
{
q.push({a[y[0]]*b[y[1]+1]+a[y[0]]*c[y[2]]+b[y[1]+1]*c[y[2]],{y[0],y[1]+1,y[2]}});
s.insert({y[0],y[1]+1,y[2]});
}
if(y[2]<n && s.find({y[0],y[1],y[2]+1})==s.end())
{
q.push({a[y[0]]*b[y[1]]+a[y[0]]*c[y[2]+1]+b[y[1]]*c[y[2]+1],{y[0],y[1],y[2]+1}});
s.insert({y[0],y[1],y[2]+1});
}
}
cout<<q.top().fi<<endl;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
solve();
return 0;
}
abc390 D-Stone XOR
题意
有 n 个袋子 ( 2 ≤ n ≤ 12 ) n个袋子(2 \leq n \leq 12) n个袋子(2≤n≤12),每个袋子有若干石头,每次操作可以将一个袋子中的石头全部放到另一个袋子中,问执行若干次操作之后(可能为0次),所有袋子中石头的数量的异或之和有多少种可能的结果
思路
-
等价于最多有12个元素,要把他们拆分到任意个数的集合之中,问这些集合的元素的异或和有多少种不同的取值
-
很经典的集合拆分问题,用 B ( n ) B(n) B(n)表示把 n n n个元素拆分的方案数,其中 B ( 12 ) ≈ 4 e 6 B(12) \approx 4e6 B(12)≈4e6,所以可以dfs枚举所有情况
-
dfs我们需要维护三个值
1.当前选择到了第几个袋子
2.当前每个集合中的石子数量
3.当前划分的集合数量 -
对于每个新考虑的元素,我们有两种策略
1.将其加入已经划分的某个集合之中
2.将其放入新集合之中
代码
// Problem: D - Stone XOR
// Contest: AtCoder - AtCoder Beginner Contest 390
// URL: https://atcoder.jp/contests/abc390/tasks/abc390_d
// Memory Limit: 1024 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define inf 0x3f3f3f3f
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
using namespace std;
const double eps=1e-6;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));
void solve()
{
int n;cin>>n;
vector<ll>a(n+1);
for(int i=1;i<=n;i++) cin>>a[i];
unordered_set<ll>s;
vector<ll>ss(n);//集合
auto dfs=[&](auto && dfs,int id,vector<ll>&cur,int used)->void
{
if(id==n+1)
{
ll ans=0;
for(auto x:cur) ans^=x;
//cout<<ans<<endl;
//for(auto x:cur) cout<<x<<" ";
//cout<<endl;
s.insert(ans);
return;
}
//将当前石子加入已有集合
for(int i=0;i<used;i++)//枚举加入的集合
{
ll last=cur[i];
cur[i]+=a[id];
dfs(dfs,id+1,cur,used);
cur[i]=last;
}
//将当前石子放到新集合
if(used<n)
{
cur[used]=a[id];
dfs(dfs,id+1,cur,used+1);
cur[used]=0;
}
};
dfs(dfs,1,ss,0);
cout<<s.size()<<endl;
//for(auto t:s) cout<<t<<endl;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
solve();
return 0;
}