P11458 [USACO24DEC] All Pairs Similarity P 题解
题目链接
题目解法
给ke磕了/bx
一步关键的转化是:\(\frac{|S\cap T|}{|S\cup T|}=\frac{|S|}{|S\cup T|}+\frac{|T|}{|S\cup T|}-1\)
我们对 \(S\) 求答案的话,就需要求 \(\sum\frac{1}{|S\cup T|}\)(\(\sum\frac{|T|}{S\cup T}\) 是类似的)
朴素的做法是对仅包含 \(S\) 的数组和出现次数数组做 \(fwt\)
需要注意到 \(fwt\) 是线性变换,即我们可以把贡献当成一条 \(\times c\) 的边,且这个变换是可逆的
于是我们相当于在一张 \(dag\) 上知道终点的取值,需要倒推出起点的值,这个是可以做的,只需要倒着做一遍 \(fwt\) 的过程就好了
时间复杂度 \(O(n+k2^k)\)
#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define all(x) x.begin(),x.end()
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
template<typename T> void read(T &FF){
FF=0;int RR=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
FF*=RR;
}
const int N=500010,P=1e9+7;
int n,k,a[N],iv[25],c1[1<<20],c2[1<<20],f[1<<20],g[1<<20];
int qmi(int x,int y){
int res=1;for(;y;y>>=1){ if(y&1) res=1ll*res*x%P;x=1ll*x*x%P;}
return res;
}
inline void inc(int &x,int y){ x+=y,x>=P&&(x-=P);}
int main(){
read(n),read(k);
F(i,1,n) read(a[i]),c1[a[i]]++,c2[a[i]]+=__builtin_popcount(a[i]);
int m=(1<<k)-1;
F(i,1,k) iv[i]=qmi(i,P-2);
F(S,1,m) f[S]=iv[__builtin_popcount(S)];
F(i,0,k-1) F(S,0,m) if(S>>i&1) inc(c1[S],c1[S^1<<i]),inc(c2[S],c2[S^1<<i]);
F(i,0,k-1) F(S,0,m) if(S>>i&1) inc(f[S^1<<i],P-f[S]);
F(S,0,m) g[S]=1ll*f[S]*c2[S]%P,f[S]=1ll*f[S]*c1[S]%P;
F(i,0,k-1) F(S,0,m) if(S>>i&1) inc(f[S^1<<i],f[S]),inc(g[S^1<<i],g[S]);
F(i,1,n){
int ans=(1ll*f[a[i]]*__builtin_popcount(a[i])+g[a[i]]-n+P)%P;
printf("%d\n",ans);
}
return 0;
}

浙公网安备 33010602011771号