你能夠進行一個串的變換,把一個長度爲\(n\)的串\(S\)能夠變成長度爲\(n-1\)的串\(T\),其中\(T_i\)要麼是\(S_i\)要麼是\(S_{i+1}\)。ios
如今問你最少進行多少次這個操做,可以使最終獲得的\(T\)只由一個字符構成。spa
\(|S|\le 100\)code
首先枚舉最終字符是哪個。那麼首先在\(S\)末尾加上一個這個字符,那麼這個最小步數等於對於全部位置而言,離它最近的枚舉的字符到這個位置的距離。遞歸
那麼直接模擬就好了,複雜度\(O(n\sum)\)。遊戲
#include<iostream> #include<cstdio> #include<cstring> using namespace std; char ch[111]; int a[111],n,ans=2e9; int main() { scanf("%s",ch+1);n=strlen(ch+1); for(int i=1;i<=n;++i)a[i]=ch[i]-97; for(int i=0;i<26;++i) { int mx=0,d=0; for(int j=n;j;--j) if(a[j]==i)d=0; else mx=max(mx,++d); ans=min(ans,mx); } printf("%d\n",ans); return 0; }
有\(n\)我的,每一個人有一頂帽子,如今每一個人會告訴你除本身外的全部人的帽子一共有多少種顏色。get
你要判斷是否存在一個合併方案知足全部人的陳述。string
\(n\le 10^5\)it
首先不難發現最大值和最小值的差最大是\(1\)。那麼咱們能夠獲得最大值的顏色一定出現了屢次,最小值的帽子一定只出現了一次。io
那麼直接大力討論一下就行了。class
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,a[100100]; void WA(){puts("No");exit(0);} int main() { n=read(); for(int i=1;i<=n;++i)a[i]=read(); sort(&a[1],&a[n+1]); if(abs(a[n]-a[1])>1){puts("No");return 0;} if(a[n]==a[1]) { if(a[n]==1||a[n]==n-1||2*a[n]<=n)puts("Yes"); else puts("No"); return 0; } int cnt=0; for(int i=n;i;--i)if(a[i]==a[n])++cnt; int v=a[n]-(n-cnt); if(v>0&&2*v<=cnt&&a[1]==v+(n-cnt)-1)puts("Yes"); else puts("No"); return 0; }
你須要構造一個\(H\times W\)的矩陣,每一個值都是\([-10^9,10^9]\)之間,要求矩陣的全部元素和是正數,且每個\(h\times w\)的子矩陣的和都是負數。
\(H,W,h,w\le 500\)
一個想法是首先把全部位置所有填滿,而後把全部\(h\times w\) 的右下角給填上一個負數知足包含這個位置的子矩形都變成負數。那麼就這樣子構造一下而後\(check\)一下是否合法。
#include<iostream> #include<cstdio> using namespace std; #define MAX 555 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int H,W,h,w; int a[MAX][MAX]; long long sum=0; int main() { H=read();W=read();h=read();w=read(); for(int i=1;i<=H;++i) for(int j=1;j<=W;++j) a[i][j]=1000; for(int i=h;i<=H;i+=h) for(int j=w;j<=W;j+=w) a[i][j]=-h*w*1000+999; for(int i=1;i<=H;++i) for(int j=1;j<=W;++j)sum+=a[i][j]; if(sum<0)puts("No"); else { puts("Yes"); for(int i=1;i<=H;++i,puts("")) for(int j=1;j<=W;++j) printf("%d ",a[i][j]); } return 0; }
給你一個數列\(a_i\),每次你能夠把\(a\) 中的一個數替換爲\(a\)中全部數的異或和。
問可否把\(a\)變成給定的\(b\)。若是能,給出最小的步驟。
\(n\le 10^5,a_i\le 2^{30}\)
首先手玩一下,能夠把這個步驟理解爲:額外補充一個\(a_{n+1}\)位置,爲前面全部數的異或和,每次操做等價於把\(i\in [1,n]\)的一個數和\(a_{n+1}\)進行交換。
那麼這樣子就能夠很容易的把\(-1\)給判掉。
對於剩下的部分,考慮每一對一一對應的位置,若是\(a_i=b_i\),那麼顯然不用管了。不然的話從\(a_i\)向\(b_i\)連一條邊,那麼這一條邊至少要貢獻一次操做。這樣子會把若干個表明值域的點連起來。首先先考慮一下特殊點的狀況,假如每一個元素都只出現了一次,那麼這樣子就會造成一堆鏈,顯然從鏈首到鏈尾一路走過去就好了,跨越鏈的時候須要額外進行一次交換操做,因此答案還須要加上聯通塊個數-1。顯然對於權值屢次出現的狀況聯通塊的問題也是同樣的。
因此答案就是邊數加上聯通塊個數減一。
注意這樣一個問題,由於最後一個元素,即初始的異或和咱們沒有連邊出去,那麼此時等於須要先進行一次交換到達某個聯通塊才能繼續操做,因此若是有這樣子的狀況的話答案要額外加一。
#include<iostream> #include<cstdio> #include<set> #include<map> using namespace std; #define MAX 100100 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } map<int,int> M; multiset<int> S; int f[MAX],tot; int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);} int ID(int x){return M[x]?M[x]:M[x]=++tot;} int n,a[MAX],b[MAX],ans=0; int main() { n=read(); for(int i=1;i<=n;++i)a[i]=read(),S.insert(a[i]),a[n+1]^=a[i]; for(int i=1;i<=n;++i)b[i]=read(),b[n+1]^=b[i]; S.insert(a[n+1]); for(int i=1;i<=n;++i) if(S.find(b[i])==S.end()){puts("-1");return 0;} else S.erase(S.find(b[i])); for(int i=1;i<=n+1;++i)f[i]=i; for(int i=1;i<=n;++i) if(a[i]!=b[i]) { int u=ID(a[i]),v=ID(b[i]); f[getf(u)]=getf(v);++ans; } if(!ans){puts("0");return 0;} for(int i=1;i<=tot;++i)if(getf(i)==i)++ans; bool fl=true; for(int i=1;i<=n;++i)if(b[i]==a[n+1])fl=false; ans+=fl;ans-=1; printf("%d\n",ans); return 0; }
有\(n\)只火雞被擺成了一排。依次來了\(m\)我的。
每一個人會進行以下操做:
若是火雞\(x_i\)和火雞\(y_i\)都還活着,那麼就等機率的吃掉其中一隻。
若是隻剩下一隻就吃掉那一隻。
若是都死了就啥都不幹。
問有多少對雞\((i,j)\)知足\(m\)我的都操做完了以後,這兩隻雞都還可能活着。
\(n\le 400,m\le 10^5\)
考慮一個枚舉任意一對以後怎麼計算,那麼顯然只要存在一我的要吃這兩隻雞中的任何一隻,那麼就直接欽定吃掉另一隻,若是不行的話那麼這一對確定不合法。
可是這樣子的複雜度是\(O(n^2m)\) 的。咱們須要尋求更加優秀的方法。
一個不難想到的想法是對於每隻雞維護一個集合,表示若是這隻雞最後想要活下來,那麼哪些雞必須死。若是咱們可以求出這個東西的話,咱們只須要判斷兩個點的集合是否有交就好了。
考慮這個東西怎麼求,若是咱們按照順序進行的話,由於咱們只欽定了這一隻雞不被吃掉,因此與這隻雞無關的雞咱們都不知道會發生什麼,那麼對於兩個集合判交的時候顯然不具有有正確性。那麼咱們時間倒流,既然這隻雞必須存活,那麼此時咱們就能夠知道在進行此次操做以前某隻雞是否必須存活,這樣子咱們就能夠獲得一個若是這隻雞最後或者,哪些雞必須不能死。
接下來再口胡一下爲何若是兩個集合有交就不合法。若是兩隻不一樣的雞不想死,那麼在這二者的集合中有一隻相同的雞不能死(注意一下這個所謂的死是指在某次操做之前不能死,也就是這隻雞爲會了救某隻特定的雞而死,那麼在救到這隻特定的雞以前這隻雞就不能死)。這裏討論一下,若是這隻雞在兩個集合中救的是同一只雞,那麼遞歸處理。不然就的雞是不一樣的,由於這隻雞隻能死一次,因此它只能救下一隻雞,因此一定有一隻雞救不活,致使目標雞也救不活。
#include<iostream> #include<cstdio> using namespace std; #define MAX 100100 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int ans,n,m,a[MAX],b[MAX]; bool alv[404][404],book[MAX]; int main() { n=read();m=read(); for(int i=1;i<=m;++i)a[i]=read(),b[i]=read(); for(int i=1;i<=n;++i) { alv[i][i]=true; for(int j=m;j;--j) { int u=a[j],v=b[j]; if(alv[i][u]&&alv[i][v]){book[i]=true;break;} if(alv[i][u]||alv[i][v])alv[i][u]=alv[i][v]=true; } } for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j) { if(book[i]||book[j])continue; bool fl=false; for(int k=1;k<=n;++k)if(alv[i][k]&&alv[j][k]){fl=true;break;} if(!fl)++ans; } printf("%d\n",ans); return 0; }
給你一個\(n\)個點\(m\)條邊的\(DAG\),問你這個\(DAG\)的全部\(2^m\)個生成子圖中,兩我的在上面玩遊戲,初始時在\(1,2\)兩個點放上一個棋子,而後把一個棋子沿着一條邊移動,不能操做者輸。
問先手勝的子圖個數。
\(n\le 15\)
顯然要考慮的是\(SG\)值那套理論,先手必勝就是兩個點\(SG\)值異或和不爲\(0\)。這個東西顯然很差算,那麼就容斥一下,改爲要算\(1,2\)兩個點的異或和必須爲\(0\)。
邊數能夠到\(O(n^2)\)級別,因此顯然不能對於邊進行狀壓。那麼考慮對於點進行狀壓。
設\(f[S]\)表示考慮點集\(S\)之間的連邊的時候\(SG(1)=SG(2)\)的方案數。
考慮怎麼進行轉移,那麼咱們顯然是要考慮兩個集合而後將他們合併。
假設兩個集合分別是\(S,T\)。顯然直接枚舉兩個集合咱們是無法作的。
不妨令必敗點集合爲\(S\),那麼其補集\(T\)就是必勝點集合。
考慮\(S,T\)以內的連邊狀況,首先\(S\)內部不能有邊(顯然必敗點之間不相鄰),而後必勝點一定存在一個後繼是必敗點,因此\(T\)中每一個點至少有一條邊連向\(S\)。而\(S\)向\(T\)連邊是隨意的。
接下來考慮\(T\)內部的連邊方案數,雖然在枚舉的時候咱們欽定了\(T\)是必勝點集合,可是單獨把\(T\)拿出來看\(T\)可能存在一些點\(SG\)值爲\(0\),因而咱們新構一個虛擬點,讓全部\(T\)中的點都連向這個虛擬點,這樣子全部點的\(SG\)值都增長了\(1\),也就所有變成了必勝點。而在前面的連邊過程當中,咱們\(T\)中任意一個點都連向了一個\(SG\)值等於零的必敗點集合\(S\),而必敗點的\(SG\)值剛好爲\(0\),所以\(T\)內部連邊且知足\(SG(1)=SG(2)\)的方案數就是\(f[T]\)。
同時注意由於\(SG(1)=SG(2)\),因此點集中必須\(1,2\)同時出現,不然就是一個不合法的狀態。
#include<iostream> #include<cstdio> using namespace std; #define MOD 1000000007 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,m,G[15],bul[1<<15],bin[25],f[1<<15]; int main() { n=read();m=read(); for(int i=1,u,v;i<=m;++i)u=read()-1,v=read()-1,G[u]|=1<<v; int N=(1<<n)-1;f[0]=1; for(int i=1;i<=N;++i)bul[i]=bul[i>>1]+(i&1); bin[0]=1;for(int i=1;i<=n;++i)bin[i]=(bin[i-1]<<1)%MOD; for(int i=2;i<=N;++i) if((i&1)==((i>>1)&1)) for(int U=i;U;U=(U-1)&i) if((U&1)==((U>>1)&1)) { int T=i^U,w=1; for(int j=0;j<n;++j) if(i&(1<<j)) { if(U&(1<<j))w=1ll*w*bin[bul[G[j]&T]]%MOD; else w=1ll*w*(bin[bul[G[j]&U]]-1)%MOD; } f[i]=(f[i]+1ll*f[T]*w)%MOD; } int ans=1;for(int i=1;i<=m;++i)ans=(ans<<1)%MOD; ans=(ans+MOD-f[N])%MOD; printf("%d\n",ans); return 0; }