【Petrozavodsk Programming Camp, Winter 2020 Problem B】 Binomial DP 詳解

There are people that wont be happy with solving a problem, unless the actual task is obscured by an elaborate
and somewhat unnecessary story. If you are one of these people, then this problem is NOT for you.
You are given a sequence of non-negative integers a1, a2, . . . , an. You need to find the number of pairs (i, j) such
that 1 ≤ i, j ≤ n and
ai
aj

is odd.
Note that n
k

is the number of ways, disregarding order, that k objects can be chosen from among n objects. In
particular, if n < k then n
k

= 0.
Input
The first line of input contains the number of test cases z (1 ≤ z ≤ 10). The descriptions of the test cases follow.
The first line of each test case contains the number of elements in the sequence n (1 ≤ n ≤ 106
).
The second line contains n integers ai (1 ≤ ai ≤ 106
) – the elements of the sequence.
Output
For each test case, output a single line which contains a single integer – the answer for the problem.
Example
standard input standard output
2
3
1 5 6
3
1 1 1
4

題意:在序列中任取兩個數使得組合數C(a[i], a[j])爲奇數, 問有多少個這樣的對

思路:

首先要知道滿足C(n,k)爲奇數的條件是n&k==k。
那麼,既然是與運算,我們就從二進制的角度考慮。要想,n&k=k,那麼肯定滿足這樣的性質,k中二進制上位上爲1的,n中對應位也肯定是1,這樣才滿足等式。那麼,完全就可以把k看成是n的一個子集,換句話說,將n的二進制上剔除若干個1可以得到k。
既然如此,我們就可以求n的一個二進制子集。怎麼求呢?我們可以每次剔除一個位。狀態轉移方程就是
dp[i] += dp[i - (1<<j)]
其中j就是i中的第j位的1 。 相當於我當前i作爲n的時候,對其有直接貢獻的就是k = i-(i<<j),而 k又可以向下分解,往它的子集取貢獻。如11010的圖示:
在這裏插入圖片描述11010 分解爲成分別去掉三個位1的子樹,子樹又可以分解成子子樹。那麼對於這棵樹上的任意一個值,若它在數列中出現過,都可以當做k,對11010提供貢獻。如序列中有2(010),那麼就可以和11010(26)組合。
按照圖示的思路,採用記憶化遞歸的代碼是:

inline ll DP(ll cur)
{
    if(vis[cur]) return vis[cur];
    if(cur==0) return 0;
    ll ans = 0;
    ll up = log2(cur) + 1;
    rep(i,0,up)
    {
        if(cur>>i&1)
        ans+= DP(cur^(1<<i));
    }
    vis[cur] = ans;    if(Map[cur]) vis[cur] += Map[cur];//Map記錄有沒有在序列中出現過
    return vis[cur] ;
}

但是遞歸會超時(雖然記憶化了),所以還是老老實實寫遞推,遞推的話就要枚舉1->最大值之間的所有點了:

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include <unordered_map>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 1e6+2;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

ll  Map[maxn];
ll a[maxn];
ll n;
int main()
{
    ll kase; kase = read();
    while(kase--)
    {
        mem(Map,0);
        n = read();
        ll ma  = -1;
        rep(i,1,n)
        {
             a[i] = read();
            Map[a[i]] ++;
            ma = max(ma, a[i]);
        }
        ll up = log2(ma) + 1;
        rep(i,0LL,up)
        {
            rep(j,1LL,ma+1)
            {
                if((j>>i)&1)
                {
                    Map[j] += Map[j - (1<<i)];
                }
            }
        }
        ll sum = 0;
        rep(i,1,n) sum += Map[a[i]];
        printf("%lld\n",sum);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章