SOS DP/Gym - 102576B
我看懂了!!
契机:排位赛B题,愣是不会求,????
Sum over subsets -> SOS
比如这样一个问题:给你一个长度为n的数组,然后统计有多少个数对 (a[i]&a[j])==a[i];
把a[j]化为2进制 100010101, 那也就是找出满足 a[j]对应位置为1,a[i]可以为1也可以为0,a[j]对应位置为0,a[i]只能为0
条件的a[i]
定义s[mask,i]表示,从右向左,从0开始,只能前i位和mask不同的个数
例子:
如果mask的第i位为0,那么s[mask,i]=s[mask,i-1]
如果mask的第i位为1,那么s[mask,i]=s[mask,i-1]+s[mask^(1<<i),i-1]
时空间优化:第二维度可以省略
#include <bits/stdc++.h> #define inf 2333333333333333 #define N 1100010 #define p(a) putchar(a) #define For(i,a,b) for(long long i=a;i<=b;++i) //by war //2020.9.28 using namespace std; long long T,n,ans; long long s[N],cnt[N],a[N]; void in(long long &x){ long long y=1;char c=getchar();x=0; while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();} while(c<='9'&&c>='0'){ x=(x<<1)+(x<<3)+c-'0';c=getchar();} x*=y; } void o(long long x){ if(x<0){p('-');x=-x;} if(x>9)o(x/10); p(x%10+'0'); } signed main(){ in(T); while(T--){ in(n); For(i,1,n){ in(a[i]); cnt[a[i]]++; } For(i,1,n) s[a[i]]=cnt[a[i]]; For(i,0,20){ For(j,0,(1<<20)){ if(j&(1<<i)){ s[j]+=s[j^(1<<i)]; } } } ans=0; For(i,1,n) ans+=s[a[i]]; o(ans);p('\n'); For(i,1,n) cnt[a[i]]=0; memset(s,0,sizeof s); } return 0; }