1. 程式人生 > 其它 >21.06.06 訓練賽

21.06.06 訓練賽

B Reverse Game

題意

​ 一個博弈遊戲,開始給定一個01字串,每次操作可以選擇如下形式中的子串:10,110,100,1010。選定後將其翻轉,即為一次操作。雙方輪流操作,某方不能操作即失敗。Alice先手,對於給定的字串,誰勝?

題解

簽到。
首先顯然,最終局面一定是如00000...11111...的形式。如果我們把相鄰兩個數交換記為一次操作,那麼題意所說的操作可以分解為一或兩次操作,如10變為01為1次操作,100變為001為2兩次操作。設\(sum\)={變成最終局面需要執行的操作次數},那麼每次操作要麼讓\(sum-1\),要麼讓\(sum-2\)。同時我們可以發現,在\(sum \geqslant 3\)

的時候,先手如果讓\(sum-1\),後手必然能夠讓\(sum-2\)。反之亦然。這就是經典博弈問題之一了。\(Ans=(sum\:mod\:3==0?Bob: Alice)\)

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e6;
char S[maxn+8];
 
int main()
{
	  scanf("%s",S+1);
	  int tmp=0,cnt=0,len=strlen(S+1);
	  for (int i=1;i<=len;i++)
		    {
			      if (S[i]=='1') tmp++;
			      else cnt=(cnt+tmp)%3;
		    }
	  cnt?puts("Alice"):puts("Bob");
	  return 0;
}

E Divisible by 3

題意

給你一個序列\([a_i]\),問有多少子序列\([l,r]\)滿足\(weight_{l,r}=\sum_{l \leqslant i\leqslant j\leqslant r}a_i \times a_j \equiv 0\pmod3\)

題解

簽到。

定義\(f_{i,j,k}\)為有多少個以i為結尾的子序列,滿足\(weight=j\ mod\ 3\),且序列和為\(k\)那麼轉移就很簡單了。具體看程式碼。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn=5e5;
int n;
ll f[maxn+8][3][3];
int a[maxn+8];

int read()
{
	  int x=0,f=1;char ch=getchar();
	  for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
	  for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	  return x*f;
}

int main()
{
	  n=read();
	  for (int i=1;i<=n;i++) a[i]=read()%3;
	  for (int i=1;i<=n;i++)
		    {
			      for (int j=0;j<3;j++)
						for (int k=0;k<3;k++)
						  f[i][(j+k*a[i])%3][(k+a[i])%3]+=f[i-1][j][k];
			      f[i][0][a[i]]++;
		    }
	  ll ans=0;
	  for (int i=1;i<=n;i++) for (int j=0;j<3;j++) ans+=f[i][0][j];
	  printf("%lld\n",ans);
	  return 0;
}

M Mistake

題意

給定n個點m條邊的有向無環圖,給一個n*k的序列,保證每個\(1 \leqslant i \leqslant n\)都在序列中出現k次。將這個序列劃分為k個序列(劃分後序列中的數相對順序不變),滿足每個序列都是這張有向無環圖的合法拓撲序。保證存在合法構造。

題解

看起來很難實際很sb的一題。

首先為了滿足拓撲序,其實就是對於序列給出了一些諸如x必須在y之前出現的限制。那麼我們考慮這樣構造:對於某一個位置上的數x,它屬於的編組序號就是數x到這個位置出現的次數。我們利用反證法證明正確性:如果存在兩個數x,y和他們第i次出現的位置\(pos_{x,i}、pos_{y,i}\),如果x必須出現在y之前而\(pos_{x,i}>pos_{y,i}\),那麼可知將\(pos_{x,i}\)也不能和\(pos_{y,i}\)之前的y放在同一編組,同時如果跟\(pos_{y,i}\)之後的某一個\(pos_{y,j}\)放在同一編組,那麼\(pos_{y,i}\)又會和\(pos_{x,j}\)衝突(因為\(i<j\),由構造方法可知\(pos_{x,j} > pos_{x,i} > pos_{y,i}\))。所以存在合法的構造當且僅當這種構造方法合法。

程式碼實現就簡單了(所以和圖有什麼關係)。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=5e5;
int id[maxn+8];
int n,k,m;

int read()
{
	  int x=0,f=1;char ch=getchar();
	  for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
	  for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	  return x*f;
}

int main()
{
	  n=read(),k=read(),m=read();
	  for (int i=1;i<=m;i++) {read(),read();}
	  for (int i=1;i<=n*k;i++)
		    {
			      int x=read();
			      printf("%d ",++id[x]);
		    }
	  return 0;
}