【BZOJ2728】[HNOI2012]与非 并查集+数位DP
【BZOJ2728】[HNOI2012]與非
Description
Input
輸入文件第一行是用空格隔開(kāi)的四個(gè)正整數(shù)N,K,L和R,接下來(lái)的一行是N個(gè)非負(fù)整數(shù)A1,A2……AN,其含義如上所述。?100%的數(shù)據(jù)滿足K≤60且N≤1000,0<=Ai<=2^k-1,0<=L<=R<=10^18
Output
僅包含一個(gè)整數(shù),表示[L,R]內(nèi)可以被計(jì)算出的數(shù)的個(gè)數(shù)
Sample Input
3 3 1 43 4 5
Sample Output
4HINT
樣例1中,(3 NAND 4) NADN (3 NAND 5) = 1,5 NAND 5 = 2,3和4直接可得。
題解:一開(kāi)始想用邏輯分析的角度來(lái)處理這道題,發(fā)現(xiàn)對(duì)于本蒟蒻來(lái)說(shuō)實(shí)在是處理不了,還是感性理解比較適合我~
我們用一個(gè)數(shù)nand它本身,就得到了這個(gè)數(shù)取非,將兩個(gè)取非的數(shù)nand一起自然就是與,有了非和與自然就有了或,有了與,非,或也自然就有了異或,所以只用nand顯然是可以表示所有邏輯運(yùn)算的。
不過(guò)這樣就能表示所有的數(shù)了嗎?顯然不能,發(fā)現(xiàn)如果集合中所有的數(shù)的某幾位是一樣的話,無(wú)論怎么運(yùn)算這幾位肯定還是一樣的,所以我們只需要統(tǒng)計(jì)有多少數(shù)的這幾位都是一樣的就行了。然后我們用并查集處理出有哪些位是一樣的,剩下的就交給數(shù)位DP就行了(又是INF的細(xì)節(jié))。
#include <cstdio> #include <iostream> #include <cstring> using namespace std; typedef long long ll; int n,k,tot,s[70]; ll ans,v[1010]; int f[70],mark[70]; int find(int x) {return (f[x]==x)?x:(f[x]=find(f[x])); } bool check(int a,int b) {for(int i=1;i<=n;i++) if(((v[i]>>a-1)^(v[i]>>b-1))&1) return 0;return 1; } ll calc(ll x) {if(++x>=(1ll<<k)) return (1ll<<s[k]);int i;ans=0;memset(mark,-1,sizeof(mark));for(i=k;i;i--){if(x&(1ll<<i-1)){if(mark[f[i]]!=1) ans+=1ll<<s[i-1];if(f[i]==i) mark[i]=1;if(mark[f[i]]==0) break;}else{if(f[i]==i) mark[i]=0;if(mark[f[i]]==1) break;}}return ans; } int main() {int j;ll i,l,r;scanf("%d%d%lld%lld",&n,&k,&l,&r);for(i=1;i<=n;i++) scanf("%lld",&v[i]);for(i=1;i<=k;i++){f[i]=i;for(j=i-1;j;j--) if(check(i,j)&&find(i)!=find(j)) f[f[j]]=f[i];}for(i=1;i<=k;i++){s[i]=s[i-1];if(find(i)==i) s[i]++;}printf("%lld",calc(r)-calc(l-1));return 0; }轉(zhuǎn)載于:https://www.cnblogs.com/CQzhangyu/p/7044514.html
總結(jié)
以上是生活随笔為你收集整理的【BZOJ2728】[HNOI2012]与非 并查集+数位DP的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 00_前情回顾
- 下一篇: 单车家族 结对项目二