[考試反思]1109csp-s模擬測試106:撞詞

(撞哈希了用了模擬測試28的詞,因此此次就叫撞詞吧)ios

藍色的0。。。ide

藍色的0。。。函數

都該聯賽了還能CE呢。。。測試

考試結束前15分鐘左右,指望得分300spa

而後對拍發現T2僞了寫了一個能拿90分的垃圾隨機化code

而後很着急,想再寫一個部分分,結果沒編譯就交了。。。blog

無論在多麼緊急的狀況下,都要檢查,編譯。遊戲

。。。還不如不對拍拿一個僞的20。。。it

而後T3少考慮一種狀況。掛了。io

 

T1:合併集合merge

區間dp板子。

 1 #include<cstdio>
 2 #include<bitset>
 3 #include<iostream>
 4 using namespace std;
 5 bitset<303>B[603][603];
 6 int n,x[603],dp[603][603],sz[603][603],ans;
 7 int main(){
 8     freopen("merge.in","r",stdin);freopen("merge.out","w",stdout);
 9     scanf("%d",&n);
10     for(int i=1;i<=n;++i)scanf("%d",&x[i]),x[i+n]=x[i];
11     for(int l=1;l<=n<<1;++l){
12         B[l][l][x[l]]=1;
13         for(int r=l+1;r<l+n&&r<=n<<1;++r)B[l][r]=B[l][r-1],B[l][r][x[r]]=1;
14     }
15     for(int l=1;l<=n<<1;++l)for(int r=l;r<l+n&&r<=n<<1;++r)sz[l][r]=B[l][r].count();
16     #define r l+len-1
17     for(int len=2;len<=n;++len)for(int l=1;r<=n<<1;++l)for(int m=l;m<r;++m)
18         dp[l][r]=max(dp[l][r],dp[l][m]+dp[m+1][r]+sz[l][m]*sz[m+1][r]);
19     for(int i=1;i<=n;++i)ans=max(ans,dp[i][i+n-1]);
20     printf("%d\n",ans);
21 }
View Code

 

T2:爬climb

除了最後一步之外,每一步必定用A-B最大的。

枚舉用的最後一個,考慮如何$O(logn)$進行$check$

發現其實只是i後面的藥丸後錯了一位,也能夠理解爲水位在i之後晚了一天才上漲。

預處理錯位先後的兩個$\sum A-B$與水位的差值,$ST$查詢。

複雜度$O(nlogn)$

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 struct P{
 5     int a,b;
 6     friend bool operator<(P x,P y){
 7         return x.a-x.b>y.a-y.b;
 8     }
 9 }p[100005];
10 int hb[100005],ans,n,lim;
11 long long L,sum[100005],h1[100005],h2[100005],wt[100005],st1[18][100005],st2[18][100005];
12 long long mn1(int l,int r){
13     if(r<l)return 123456789012345;
14     int B=hb[r-l+1];
15     return min(st1[B][l],st1[B][r-(1<<B)+1]);
16 }
17 long long mn2(int l,int r){
18     if(r<l)return 123456789012345;
19     int B=hb[r-l+1];
20     return min(st2[B][l],st2[B][r-(1<<B)+1]);
21 }
22 int main(){freopen("climb.in","r",stdin);freopen("climb.out","w",stdout);
23     scanf("%d%lld",&n,&L);ans=lim=n+1;//printf("n=")
24     for(int i=1;i<=n;++i)scanf("%d%d",&p[i].a,&p[i].b);
25     sort(p+1,p+1+n);
26     for(int i=1;i<=n;++i)scanf("%lld",&wt[i]),wt[i]+=wt[i-1];
27     for(int i=1;i<=n;++i)sum[i]=sum[i-1]+p[i].a-p[i].b;//,printf("%lld\n",sum[i]);
28     for(int i=1;i<=n;++i)if(sum[i]<sum[i-1]){lim=i;break;}
29     for(int i=1;i<=n;++i)h1[i]=h2[i]=sum[i];
30     for(int i=1;i<=n;++i)h1[i]-=wt[i],h2[i]-=wt[i-1];
31     for(int i=1;i<=n;++i)st1[0][i]=h1[i],st2[0][i]=h2[i];
32     for(int j=1;j<18;++j)for(int i=1;i+(1<<j)-1<=n;++i)st1[j][i]=min(st1[j-1][i],st1[j-1][i+(1<<j-1)]);
33     for(int j=1;j<18;++j)for(int i=1;i+(1<<j)-1<=n;++i)st2[j][i]=min(st2[j-1][i],st2[j-1][i+(1<<j-1)]);
34     for(int i=0;i<=18;++i)for(int j=1<<i;j<1<<i+1&&j<=n;++j)hb[j]=i;
35     for(int i=1;i<=n;++i){//printf("%d/%d:%d %d\n",i,n,p[i].a,p[i].b);
36         int out1=lower_bound(sum,sum+lim,L-p[i].a)-sum,out2=lower_bound(sum,sum+lim,L-p[i].b)-sum;//printf("%d %d %d\n",out1,out2,lim);
37         if(out1==lim&&out2==lim)continue;
38         if(out1<i&&out1!=lim){//printf("%lld\n",mn1(1,out));
39             if(mn1(1,out1)<=0)continue;
40             ans=min(ans,out1+1);//printf("1:%d %d\n",i,out1);
41         }else if(out2>=i&&out2!=lim){//printf("%lld %lld\n",mn1(1,i-1),mn2(i+1,out));
42             if(mn1(1,i-1)<=0)continue;
43             if(mn2(i+1,out2)<=p[i].a-p[i].b)continue;
44             ans=min(ans,out2);//printf("2:%d %d\n",i,out2);
45         }
46     }printf("%d\n",ans==n+1?-1:ans);//printf("%lld %lld\n",mn1(1,5),mn2(7,8));
47 }
View Code

 

T3:硬幣coin

簡單的博弈論。主要不在博弈論,而在轉化爲圖論。

相似2-SAT的思想(但其實並沒什麼關係),把每個行列都拆成2個點,分別表示選與不選。

連邊表示若是存在狀態A,那麼狀態B必定存在。

根據每個硬幣,若是它是正面,那麼若是選這一行那麼就必須選這一列,若是不選這一行就不能選這一列。

雙向邊$A_1 - B_1$與$A_0 - B_0$。

若是硬幣是反面,那麼就是$A_1 - B_0$與$A_0 - B_1$

連邊以後圖會成爲若干個聯通塊,互不干擾。

如今的問題就是肯定每個聯通塊的狀態。

若是一個聯通塊內部矛盾(既要選又要不選)那麼答案直接根據奇偶判斷01。

不然,若是一個聯通塊內無論怎麼肯定狀態,須要選的行列都是偶數(簡稱偶偶),那麼對答案沒有影響。

若是存在奇偶塊,那麼拿到最後一個奇偶塊的是必勝的(你能夠經過這一塊決定全局的步數是奇數仍是偶數)

若是沒有奇偶塊,那麼若是奇奇塊的數量是奇數,那麼先手必勝3。不然2。

接下來就考慮如何爭奪奇偶塊。由於拿到最後一個奇偶塊就贏了,因此兩我的都不但願拿到倒數第二個奇偶塊,那麼就想拿到倒數第三個。

同理推下去,若是奇偶塊一共有奇數個,那麼先手者必定能拿到最後一個奇偶塊,輸出3。

不然,奇偶塊一共有偶數個,那麼先手者會盡可能避免拿到第一個奇偶塊。

因此爲了避免拿第一個奇偶塊,他會去拿奇奇塊。(拿偶偶塊沒有影響)

拿完奇奇塊以後就輪到對手了,對手也同理不想拿奇偶,因此他也會拿奇奇。

因此兩我的如今開始搶奇奇塊了。

若是奇奇塊有奇數個,那麼先手者就能強制後手者拿走第一個奇偶塊,就能夠取勝,3。不然就是2。

全部狀況討論完畢。

 1 #include<cstdio>
 2 int fir[405],l[200005],to[200005],ec=1,pc=1,ord[2][102],ok=1,al[405],sz,cnt,blcnt,sz2,g,sg;
 3 char s[103][103];
 4 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;}
 5 void Link(int a,int b){link(a,b);link(b,a);}
 6 void dfs(int p){
 7     al[p]=1;sz++;sz2+=p&1;
 8     for(int i=fir[p];i;i=l[i])if(al[to[i]]==0)dfs(to[i]);
 9 }
10 int main(){
11     freopen("coin.in","r",stdin);freopen("coin.out","w",stdout);
12     int t,n,m;
13     scanf("%d",&t);
14     while(t--){
15         scanf("%d%d",&n,&m);
16         for(int i=1;i<=n;++i)scanf("%s",s[i]+1);
17         for(int i=1;i<=n;++i)pc+=2,ord[0][i]=pc;
18         for(int i=1;i<=m;++i)pc+=2,ord[1][i]=pc;
19         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)
20             if(s[i][j]=='o')Link(ord[0][i],ord[1][j]),Link(ord[0][i]^1,ord[1][j]^1);
21             else if(s[i][j]=='x')Link(ord[0][i],ord[1][j]^1),Link(ord[0][i]^1,ord[1][j]);
22         for(int i=2;i<=pc;++i)if(al[i]==0&&al[i^1]==0){
23             sz2=sz=0,dfs(i),cnt+=sz&1,blcnt++,g+=(sz2&1)&(sz-sz2&1);
24             if((sz2&1)&&(sz-sz2&1))sg^=1;
25             if(sz&1)sg^=2;
26         }
27         for(int i=2;i<=pc;++i)if(al[i]&&al[i^1])ok=0;
28         if(ok)puts(sg?"3":"2");
29         else puts(m+n&1?"1":"0");
30         for(int i=2;i<=pc;++i)fir[i]=al[i]=0;pc=ec=ok=1;cnt=blcnt=g=sg=0;
31     }
32 }
View Code

從博弈論的角度出發如何解釋?

mex函數:mex(X)=X中不存在的最小非負整數。如mex(1,2)=0,mex(0)=1...

SG函數:爲0時表明必敗局面,不然爲必勝局面。計算方法爲mex(通向的全部狀態)

整場遊戲若是能夠分紅若干不相關的部分,那麼整場遊戲的SG函數值就是每一個子遊戲的SG函數值異或和。

在這道題裏,子游戲就是一個聯通塊。

對於偶偶塊,不論怎麼選先手都會輸,走一步後剩奇數步,那麼SG(next)=1,SG=mex(SG(next))=0。

對於奇奇塊,不論怎麼選先手都會贏,走一步後剩偶數步,那麼SG(next)=0,SG=mex(SG(next))=1。

對於奇偶塊,先手走一步以後,剩餘奇偶步都有可能,那麼SG(next)={1,0},SG=mex(SG(next))=2。

計算出每個聯通塊的SG函數,異或一下,ans=2+(SG?1:0)

固然,若是不存在全部硬幣都正面朝上的狀況,仍是要特判的。

相關文章
相關標籤/搜索