1. 程式人生 > >[bzoj4762]最小集合

[bzoj4762]最小集合

題目描述

定義一個非空集合是合法的,當且僅當它滿足以下兩個條件。
1、集合內所有元素and和為0
2、它的非空子集中僅有它本身滿足1
給出一個集合S,求它的合法非空子集數。

DP

先把給定集合所有數取反。
比如有效位數是4位,1101就變成0010。
那麼問題變成,所有元素or和為1023,而去掉任意一個元素後or和均不為1023。
那麼接下來我們來設一個詭異的狀態。
因為要知道去掉一個人元素會不會使or和為1023,因此我們前後都要知道。
可以設一個f[i,j,k]表示做完了i個數,前面選擇的一些數or和為j,我們希望後面選出的數or和為k。
然而隨便推一推都覺得不會轉移啊。。
這時趕緊改一下狀態,設f[i,j,k]表示做完了i個數,前面選擇的一些數or和為j,我們希望後面選出的數or和包含k(什麼叫包含?x包含k需滿足x&k=k)
設第i+1個數為x。
不選?f

[i+1][j][k]+=f[i][j][k]
選呢?
假設後面部分不包含x時是k’。
我們發現k’需要滿足兩個條件:
1、k’|x=k(根據狀態定義)
2、k’|j|x!=k’|j(如果等了那麼就能去掉x了)
滿足條件的k’肯定存在一些包含關係,而我麼的狀態設的也是包含,所以轉移會比較方便。
先只考慮滿足第一個條件:
f[i+1][j|x][k^(k&x)]+=f[i][j][k]
這個很容易考慮,如果k的某一位有1,x該位也有1,那麼k’的這一位可以是0也可以為1。
再去掉滿足第一個條件而不滿足第二個條件的:
f[i+1][j|x][(k^(k&x))|(x^(x&j))]-=f[i][j][k]
這是個什麼意思?先看後面,顯然只有x的某一位是1,而j的對應位是0時才有1的貢獻,意思就是x會給j的哪些原本沒有1的位變成1。
而如果k’的這些位也有1,那麼j|k’後,再或x將不變,因此會不滿足第二個條件。
然後這個dp就是正確的,初始f[0][0][0]=1,最後答案是f[n][1023][0]。
假設有m位,這樣做是n
4m

考慮優化吧。我們來證明,如果f[i][j][k]不為0,一定有j包含k。
初始時顯然滿足。
看第一條轉移,f[i+1][j|x][k^(k&x)]+=f[i][j][k]。
假如j包含k,k的某位為0時,k&x的對應位肯定也是0,所以k^(k&x)並不會多1,反而可能少1,但j|x不會少1,因此有j|x包含k^(k&x)。
看第二條轉移,f[i+1][j|x][(k^(k&x))|(x^(x&j))]-=f[i][j][k]。
假如j包含k,記k’=k^(k&x)。假如k’某位為1,可以不管它,由上面的結論j|x的這位也會是1。假如k某位為0,k’這位也是0,而x^(x&j)是1,那麼最終這位會是1,而因為x^(x&j)的這位是1,x這位必須是1,那麼j|x這位也有1了。
這樣列舉j後每次只需列舉一個j包含的k,有個快捷的列舉方法見程式碼。
那麼可以證明覆雜度降為n
3m

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=1000+10,N=1023,mo=1000000007;
int a[maxn],f[N+10][N+10],g[N+10][N+10];
int i,j,k,l,t,n,m,ans,x;
int main(){
    scanf("%d",&n);
    fo(i,1,n) scanf("%d",&a[i]),a[i]^=N;
    f[0][0]=1;
    fo(i,1,n){
        fo(j,0,N){
            k=j;
            while (1){
                g[j][k]=0;
                if (!k) break;
                k=(k-1)&j;
            }
        }
        x=a[i];
        fo(j,0,N){
            //if ((x&j)==x) continue;
            k=j;
            while (1){
                (g[j|x][k^(k&x)]+=f[j][k])%=mo;
                (g[j|x][(k^(k&x))|(x^(x&j))]-=f[j][k])%=mo;
                if (!k) break;
                k=(k-1)&j;
            }
        }
        fo(j,0,N){
            k=j;
            while (1){
                (f[j][k]+=g[j][k])%=mo;
                if (!k) break;
                k=(k-1)&j;
            }
        }
    }
    ans=f[N][0];
    (ans+=mo)%=mo;
    printf("%d\n",ans);
}

相關推薦

[bzoj4762]集合

題目描述 定義一個非空集合是合法的,當且僅當它滿足以下兩個條件。 1、集合內所有元素and和為0 2、它的非空子集中僅有它本身滿足1 給出一個集合S,求它的合法非空子集數。 DP 先把給定集合所有數取反。 比如有效位數是4位,1101就變成00

BZOJ4762 集合(動態規劃+容斥原理)

容斥 amp () cpp ret urn long .cn a.out   https://www.cnblogs.com/AwD-/p/6600650.html #include<iostream> #include<cstdio>

bzoj4762】【JZOJ5151】集合 題解

轉自 AwD! 的部落格 題目大意 定義一個非空集合是合法的,當且僅當它滿足以下兩個條件。 1、集合內所有元素 and 和為 0 2、它的非空子集中僅有它本身滿足 1 給出一個集合 S,求它的合法非空子集數。 (這裡的集合都是可重的)

51nod 1616 集合(枚舉倍數)

ima 個數 return 復雜度 amp ems ios 比較 esp 分析:也就是取任意多個數,它們的最大公約數都在這個集合裏。考慮到ai比較小,可以枚舉小於a中最大值的所有數,判斷是否為其中若幹個數的gcd。記c[k]為a中k的倍數的個數,然後枚舉k的倍數i*k,c

51NOD 1616 集合

com int ret include stream 發現 傳送門 ctype () 傳送門 分析 不難發現集合中的數一定是集合內其它一堆數的$gcd$ 於是我們枚舉$i$,統計原來集合中有幾個數是$i$的倍數,設這個值為$f(i)$ 之後對於每個$i$如果不存在$f(x*

51nod-1616 集合(數論)

基準時間限制:1 秒 空間限制:131072 KB 分值: 80 難度:5級演算法題  收藏  關注 A君有一個集合。 這個集合有個神奇的性質。 若X,Y屬於該集合,那麼X與Y的最大公因數也屬於該集合。 但是他忘了這個集合中原先有哪些數字。 不過幸運的是,他

集合中查找前k個的數

swa n) print style swap fin ++ sizeof out 1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define SIZE 10 5 6 int * k

Collection.max min集合(list set map)

//list 用來待儲存檔案表中檔案id List<Integer> list=new ArrayList<>(); //file

證明兩個集合的劃分絕對值差問題

總的 解空間的大小 是 C(2n, n)/2  =  2n!/(n!*n!)/2 從2n個元素中取出n個元素的組合數目,  又由於對稱性 , 所以除以2 例如: 1234 ---》 取出兩個元素的組合:  12  13 14 23 24 34 分成兩個集合的可能性是:

在一個列表中儲存以下元素:apple,grape,banana,pear 3.1 返回集合中的大的和的元素 3.2 將集合進行排序,並將排序後的結果列印在控制檯上 [必做題]

比較類 public class name implements Comparator<f>{ @Override public int compare(f o1, f o2) { return o2.getName().compareTo(o1.get

無向圖:查詢集合(短路徑回溯演算法)

         在無向圖中查詢最小環,就像需要查詢一個蜂窩中所有孔洞,如果只查詢數目,可以利用尤拉公式,若查詢到所有環,需要更進一步的搜尋。          方法:尋找到所有頂點的最短路徑,對每一個頂點,取出環,迴圈刪除頂點,輸出所有最小環。          注意:拓

從一個集合中查詢的N個元素——Python heapq 堆資料結構

Top N問題在搜尋引擎、推薦系統領域應用很廣, 如果用我們較為常見的語言,如C、C++、Java等,程式碼量至少也得五行,但是用Python的話,只用一個函式就能搞定,只需引入heapq(堆佇列)這個資料結構即可。今天偶然看到這個庫,特意記下之。 先看一個例子: 複製

計算二維空間中點的集合凸包

from scipy import spatial import numpy as np import matplotlib.pyplot as plt np.random.seed(42) points2d=np.random.rand(10,2)#一組二維平面上的隨機

42.C#--集合的使用,創建一個集合,裏面添加一些數字,求平均值與和,以及大值,

一個 void n) write 賦值 += 最大 tel count static void Main(string[] args){//42.集合的使用,創建一個集合,裏面添加一些數字,求平均值與和,以及最大值,最小值//創建一個集合ArrayList list = n

使用Java Stream,提取集合中的某一列/按條件過濾集合/求和/大值/值/平均值

不得不說,使用Java Stream操作集合實在是太好用了,不過最近在觀察生產環境錯誤日誌時,發現偶爾會出現以下2個異常: 1. java.lang.NullPointerException 2. java.util.NoSuchElementException 因此本篇部落格總結下使用Java Stre

List使用Stream流進行集合Collection的各種運算彙總:對BigDecimal求和,某個欄位的和、大值、值、平均值,欄位去重,過濾等

       寫Java介面的朋友都知道,Java 8的更新,經常會用到過濾 list<Object> 裡的資料,本文就對List使用Stream流進行集合Collection的各種運算做一個彙總! 優勢:       &nbs

6、劍指offer--旋轉數組的數字

namespace 數字 log clu 有序 ios end esp offer 題目描述 把一個數組最開始的若幹個元素搬到數組的末尾,我們稱之為數組的旋轉。 輸入一個非遞減排序的數組的一個旋轉,輸出旋轉數組的最小元素。 例如數組{3,4,5,1,2}為{1,2,3,4,

三分鐘學會用SpringMVC搭建系統(超詳細)

springmvc+mybatis dubbo+zookeeper restful redis分布式緩存 kafka 前言做 Java Web 開發的你,一定聽說過SpringMVC的大名,作為現在運用最廣泛的Java框架,它到目前為止依然保持著強大的活力和廣泛的用戶群。本文介紹如何用ecli

[POJ3041] Asteroids(點覆蓋-匈牙利算法)

mes 技術分享 set || tdi line isp none event 傳送門 題意: 給一個N*N的矩陣,有些格子有障礙,要求我們消除這些障礙,問每次消除一行或一列的障礙,最少要幾次。 解析: 把每一行與每一列當做二分圖兩邊的點。

BZOJ 4883 [Lydsy2017年5月月賽]棋盤上的守衛(生成環套樹森林)

print 我們 size -s nbsp long pan typedef 包含 【題目鏈接】 http://www.lydsy.com/JudgeOnline/problem.php?id=4883 【題目大意】   在一個n*m的棋盤上要放置若幹個守衛