日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

NOIP模拟测试22「位运算」

發布時間:2023/12/2 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NOIP模拟测试22「位运算」 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

范圍n-----$100000$ ? m $30$

輸出方案

這是一個很好的$dp$題

首先我們應該看出來一條性質只要你最后有方案達到$n$個$1$,那么你可以達到任何`一種$n$個$1$的情況

例如

你最后可以達到$3$個$1$

那么你可以達到$11100 $? $ 01110$ ?? $01011$ ?? $01101$等方案

證明:題目里說的操作和優美值都是一的個數相關,只要我們可以達到$n$個一,我們可以通過變換$1$的位置得到多種方案

于是題目里給的$C$我們并不關心,我們只關心$1$的個數,我們把1都放到最右面,另外這也給我們下面dp提供方便

我們先看各個運算意義

假設你$x$和$y$中有$s$個重復的$1$求各個運算之后一的個數,

$\&$后$1$的個數就是$s$,

對于$|$我們如果都加的話重復的多加一次

$|$后$1$的個數就是$x+y-s$

重復變為0

^后$1$的個數就是$x+y-s-s$

然后我們可以嘗試列一個$dp$

設$f[x][j]$表示第$x$個操作有$j$個$1$的情況,我們當有這種情況就設為$1$,設最后$C$中一的個數為$x$,我們只需要看$f[n][x]$,若$f[n][x]$為$1$遞歸找解(后面會說到,其實遞歸找解是這個題最難的地方),若為$0$就是無解

根據上面得出結論我們可以列出

枚舉這次操作后重復個數為w,設這次優美值為$G[i]$,枚舉之前1的個數為$j$

若這次操作為$\&$則$f[x][w]=max(f[x-1][j],f[x][w])$

若為$|$ 則$f[x][G[i]+j-w]=max(f[x][G[i]+j-w],f[x-1][j])$

類似的若為^則$f[x][G[i]+j-w*2]=max(f[x][G[i]+j-w*2],f[x-1][j])$

注意一下邊界以及循環枚舉

for(ll w=max(0ll,G[i]+j-m);w<=min(j,G[i]);w++)

看這句max,因為當它比m大時一定有重疊部分,且重疊部分至少為$G[i]+j-m$

然后就到了這個題難點記錄方案

思考我們已知條件

我們從后往前搜,我們目前知道的是最終值,我們每一次都把前一位的值算出來,再搜前一位

那么我們已知運算符號已知運算前后1的個數,和運算后的值,我們現在要做的就是求出前一位值,且我們知道這一定能找到一組合法解

三個運算符號分開考慮,

假設符號為$|$,我們已知當前1個數(G[i]),和這次操作之后1的個數,這次操作之前1的個數

我們只需要讓這次操作之前數,這次操作數二進制下1完美覆蓋掉這次操作1的個數即可

因為我們符號$|$,我們只需要一個從前掃,一個從后掃,類似ST重復無所謂

例如操作前$3$個$1$,這次操作$3個1$操作后$11011$,我們一個從前掃得到$11010$另一個得到$01011$

假設符號為$\&$,&只會讓數值變小,我們可以斷定操作前1的個數和這次操作1一定>=操作后

我們先保證這些操作后上有值的位一定有$1$,然后我們分開放剩下的$1$

例如操作前$4$個$1$,這次操作$3$個$1$,操作后$10001$,我們先得到$10001$?? $10001$,再分開放1得到$11101$和$10011$

假設我們當前符號^,思考^運行他會使相同的位置變成0,不同變為1

那么我們重復的地方可以算出是(之前1的個數+這次1的個數-操作后1的個數)/2

然后先給不重復地方賦值,再分別給重復地方賦1

例如操作前$4$個$1$,這次操作$2$個$1$,操作后$11101$,我們先得到重復長度1,然后得到$11100$和$00001$,最后給重復賦值$11110$,$00011$

void dfs(ll pos,ll now) {if(pos==1){printf("%lld ",now);return ;}ll cnt_1=0; // printf("pos=%lld\n",pos);for(ll i=1;i<=m;i++)if(now&(1<<(i-1)))cnt_1++;if(ch[pos][1]=='A'){ll nowk=now,tot=cnt_1,pr=pre[pos][cnt_1],nxtk=now;//知道目前的數,知道目前cnt,知道之前cnt,求之前數//因為是&所以在滿足k條件下盡量差開,分開放最優//例如10001//先放11001再放10111for(ll i=1;i<=m;i++){if(tot==G[pos]) break;if(!(now&(1<<(i-1))))nowk|=(1<<(i-1)),tot++;}tot=cnt_1;for(ll i=1;i<=m;i++){if(tot==pr) break;if(!(nowk&(1<<(i-1))))nxtk|=(1<<(i-1)),tot++;} // printf("nxtk=%lld nowk=%lld\n",nxtk,nowk);dfs(pos-1,nxtk);printf("%lld ",nowk);}if(ch[pos][1]=='O'){ll nowk=0,tot=0,pr=pre[pos][cnt_1],nxtk=0;//知道目前的數,知道目前cnt,知道之前cnt,求之前數//那么向kmp一樣或無所謂for(ll i=1;i<=m;i++){if(tot==G[pos])break;if(now&(1<<(i-1)))nowk|=(1<<(i-1)),tot++;}tot=0;for(ll i=m;i>=1;i--){if(tot==pr)break;if(now&(1<<(i-1)))nxtk|=(1<<(i-1)),tot++;} // printf("nx=%lld no=%lld\n",nxtk,nowk);dfs(pos-1,nxtk);printf("%lld ",nowk);}if(ch[pos][1]=='X'){ll nowk=0,tot=0,pr=pre[pos][cnt_1],nxtk=0,lst=0;ll chong=(pr+G[pos]-cnt_1)/2;//計算出重合部分,重合部分就是第一個長度+第二個長度-總1數/2for(ll i=1;i<=m;i++){if(tot==G[pos]-chong)break;if(now&(1<<(i-1)))nowk|=(1<<(i-1)),tot++,lst=i;}tot=0;for(ll i=m;i>=lst+1;i--){if(tot==pr-chong)break;if(now&(1<<(i-1)))nxtk|=(1<<(i-1)),tot++;}tot=0;for(ll i=1;i<=m;i++){if(tot==chong)break;if(!(now&(1<<(i-1))))nowk|=(1<<(i-1)),nxtk|=(1<<(i-1)),tot++;} // printf("nxk=%lld nok=%lld\n",nxtk,nowk);dfs(pos-1,nxtk);printf("%lld ",nowk);} } 求方案代碼

總代碼

#include<bits/stdc++.h> using namespace std; #define ll long long #define A 1010101 ll f[A][32],pre[A][32],G[A],dl[A]; ll x,n,m,c; char ch[A][6]; bitset<36> b; void dfs(ll pos,ll now) {if(pos==1){printf("%lld ",now);return ;}ll cnt_1=0; // printf("pos=%lld\n",pos);for(ll i=1;i<=m;i++)if(now&(1<<(i-1)))cnt_1++;if(ch[pos][1]=='A'){ll nowk=now,tot=cnt_1,pr=pre[pos][cnt_1],nxtk=now;//知道目前的數,知道目前cnt,知道之前cnt,求之前數//因為是&所以在滿足k條件下盡量差開,分開放最優//例如10001//先放11001再放10111for(ll i=1;i<=m;i++){if(tot==G[pos]) break;if(!(now&(1<<(i-1))))nowk|=(1<<(i-1)),tot++;}tot=cnt_1;for(ll i=1;i<=m;i++){if(tot==pr) break;if(!(nowk&(1<<(i-1))))nxtk|=(1<<(i-1)),tot++;} // printf("nxtk=%lld nowk=%lld\n",nxtk,nowk);dfs(pos-1,nxtk);printf("%lld ",nowk);}if(ch[pos][1]=='O'){ll nowk=0,tot=0,pr=pre[pos][cnt_1],nxtk=0;//知道目前的數,知道目前cnt,知道之前cnt,求之前數//那么向kmp一樣或無所謂for(ll i=1;i<=m;i++){if(tot==G[pos])break;if(now&(1<<(i-1)))nowk|=(1<<(i-1)),tot++;}tot=0;for(ll i=m;i>=1;i--){if(tot==pr)break;if(now&(1<<(i-1)))nxtk|=(1<<(i-1)),tot++;} // printf("nx=%lld no=%lld\n",nxtk,nowk);dfs(pos-1,nxtk);printf("%lld ",nowk);}if(ch[pos][1]=='X'){ll nowk=0,tot=0,pr=pre[pos][cnt_1],nxtk=0,lst=0;ll chong=(pr+G[pos]-cnt_1)/2;//計算出重合部分,重合部分就是第一個長度+第二個長度-總1數/2for(ll i=1;i<=m;i++){if(tot==G[pos]-chong)break;if(now&(1<<(i-1)))nowk|=(1<<(i-1)),tot++,lst=i;}tot=0;for(ll i=m;i>=lst+1;i--){if(tot==pr-chong)break;if(now&(1<<(i-1)))nxtk|=(1<<(i-1)),tot++;}tot=0;for(ll i=1;i<=m;i++){if(tot==chong)break;if(!(now&(1<<(i-1))))nowk|=(1<<(i-1)),nxtk|=(1<<(i-1)),tot++;} // printf("nxk=%lld nok=%lld\n",nxtk,nowk);dfs(pos-1,nxtk);printf("%lld ",nowk);} } int main(){scanf("%lld%lld%lld",&n,&m,&c);for(ll i=2;i<=n;i++){scanf("%s",ch[i]+1);}b|=c;x=b.count(); // printf("x=%lld\n",x);for(ll i=1;i<=n;i++){scanf("%lld",&G[i]);}f[1][G[1]]=1;for(ll i=2;i<=n;i++)for(ll j=0;j<=m;j++){if(!f[i-1][j]) continue;for(ll w=max(0ll,G[i]+j-m);w<=min(j,G[i]);w++){if(ch[i][1]=='A'){if(f[i-1][j]==1)f[i][w]=1,pre[i][w]=j/*,printf("A pos=%lld pre[%lld][%lld]=%lld\n",i,i,w,pre[i][w])*/;}if(ch[i][1]=='O'){if(f[i-1][j]==1)f[i][j+G[i]-w]=1,pre[i][j+G[i]-w]=j/*,printf("O pos=%lld pre[%lld][%lld]=%lld\n",i,i,j+G[i]-w,pre[i][j+G[i]-w])*/;}if(ch[i][1]=='X'){if(f[i-1][j]==1)f[i][j+G[i]-2*w]=1,pre[i][j+G[i]-2*w]=j/*,printf("X pos=%lld pre[%lld][%lld]=%lld\n",i,i,j+G[i]-2*w,pre[i][j+G[i]-2*w])*/;}}}dfs(n,c); } View Code

?

轉載于:https://www.cnblogs.com/znsbc-13/p/11361565.html

總結

以上是生活随笔為你收集整理的NOIP模拟测试22「位运算」的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。