P3067 [USACO12OPEN] Balanced Cow Subsets G
题解
设前半部分对两个集合贡献的差为a,后半部分贡献为b
若 \(a==b\) 则
差为a的组合数(被选上,和在哪个集合无关)sa 与b的组合数的sb
此时对答案的贡献为 \(sa·sb\)
所以穷举所有差的组合,然后累加
设差为集合A-集合B
每个元素对差的贡献有三种可能,要么加要么减要么不加也不减
由于只要用过就算进组合里(无论在哪个集合),所以差的正负值不重要(这是代码特性决定的)
而且同一组合可以诞生不同的差
code1
#define ll long long
#include<bits/stdc++.h>
using namespace std;
// Custom input and output functions
inline void read(ll &x) {
x = 0;
ll flag = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-')flag = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
x *= flag;
}
inline void write(ll x)
{
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
ll n, mid;
ll a[28] = {0};
map<int,int> id;
ll judge[(1<<22)+5] = {0};
ll len = 0;
vector<ll> use[(1<<22)+5];
void ss1(ll x, ll sum, ll now)
{
if(x > mid)
{
if(!id[sum]) id[sum] = ++len;
use[id[sum]].push_back(now);
return;
}
ss1(x + 1, sum + a[x], now | (1LL << (x - 1)));
ss1(x + 1, sum - a[x], now | (1LL << (x - 1)));
ss1(x + 1, sum, now);
}
void ss2(ll x, ll sum, ll now)
{
if(x > n)
{
ll who = id[sum];
if(who)
for(ll i = 0; i < use[who].size(); i++)
judge[now | use[who][i]] = 1;
return;
}
ss2(x + 1, sum + a[x], now | (1LL << (x - 1)));
ss2(x + 1, sum - a[x], now | (1LL << (x - 1)));
ss2(x + 1, sum, now);
}
int main()
{
read(n);
for(ll i = 1; i <= n; i++) read(a[i]);
mid = n >> 1; // Optimized division by 2
ss1(1, 0, 0);
ss2(mid + 1, 0, 0);
ll ans = 0;
for(ll i = 1; i <= (1LL << n) ; i++) ans += judge[i];
write(ans);
putchar('\n');
return 0;
}
code2
#define ll long long
#include<bits/stdc++.h>
using namespace std;
inline void read(ll &x) {
x = 0;
ll flag = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-')flag = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
x *= flag;
}
inline void write(ll x)
{
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
ll n, mid,ans=0;
ll a[13] = {0} ,b[13]={0};
unordered_map<ll, bitset<1024> >haxi;
bitset<1024> use1[1024];
ll len = 0;
void ss1(ll x, ll sum, ll now)
{
if(x > mid)
{
haxi[sum].set(now);//把sum下的这种可能点亮
return;
}
ss1(x + 1, sum + a[x], now | (1LL << (x - 1)));
ss1(x + 1, sum - a[x], now | (1LL << (x - 1)));
ss1(x + 1, sum, now);
}
void ss2(ll x, ll sum, ll now)
{
if(x > n-mid)
{
bitset<1024> use (haxi[sum]);//代表前半部分达到sum的有哪些可能的组合
use&=~use1[now];//去重,use1代表当后半部分的组合为now时,它(指代后半部分组合)在此之前已经和哪些前半部分组合过了
ans+=use.count();
use1[now]|=use;
return;
}
ss2(x + 1, sum + b[x], now | (1LL << (x - 1)));
ss2(x + 1, sum - b[x], now | (1LL << (x - 1)));
ss2(x + 1, sum, now);
}
int main()
{
read(n);mid = n >> 1;
for(ll i = 1; i <= mid; i++) read(a[i]);
for(ll i = 1; i <= n-mid; i++) read(b[i]);
ss1(1, 0, 0);
ss2(1, 0, 0);
write(ans-1);
putchar('\n');
return 0;
}

浙公网安备 33010602011771号