CF補題[長期更新]
現在主要寫div2的題,從前往後寫到實在不會寫,思路很簡單的就不記錄了
- E. Game with String
題意:兩個人玩遊戲,AB都知道原字串s,A將s從下表為k的位置斷開,左右交換組成新的字元穿t
B猜k,求b的贏得機率
B有兩個手段
1.先看新字串t的首字母也就是s[k+1]
2.看完首字母后 決定看t的任意一個字元
最後求B能百分百猜對t的概率
簡單的博弈概率題目
圖書館要關了,先存檔一下,明天再寫
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#define mem(x) memset(x,0,sizeof(x))
using namespace std;
const int N=5e3;
int n,len;
char str[N+10];
char ss[N+10];
bool have[26];
vector<string> arr[26];
void merge(char *s,int t)
{
int top=0;
for(int i=t;i<len;i++)
s[top++]=str[i];
for (int i=0;i<t;i++)
s[top++]=str[i];
s[top]='\0';
}
int main()
{
while(~scanf("%s",str))
{
//init
mem(have);
for(int i=0;i<26;i++)arr[i].clear();
len=0;
//get len
for(int i=0;str[i]!='\0';i++)
{
len++;
have[str[i]-'a' ]=true;
}
//得到字串矩陣
int top=0;//arr的下標
for(int i=0;i<26;i++)if(have[i])
{
char c='a'+i;
for(int j=0;j<len;j++)
{
if(str[j]==c)
{
merge(ss,j);
printf("%s\n",ss);
arr[top].push_back(string(ss));
}
}
top++;
}
double ans = 0;
for(int i=0;i<top;i++)
{
int len1 = arr[i].size();
int maxt=0;
for(int j=1;j<len;j++)
{
int cnt[26];
mem(cnt);
int tt=0;
for(int k=0;k<len1;k++)
{
char tp = arr[i][k][j]-'a';
cnt[tp]++;
}
for(int k=0;k<26;k++)if(cnt[k]==1)tt++;
maxt = max(maxt,tt);
}
ans+=(double)maxt;
}
double p = ans/len;
printf("%.14f\n",p);
}
return 0;
}
題意有點誤會,所以寫錯了,在所有字串排列成的矩陣中
比如
actactictict
actictictact
ctictactacti
ctactacticti
ctacticticta
ctictictacta
ictictactact
ictactactict
tictictactac
tictactactic
tactactictic
tactictictac
ac程式碼只要對所有行sigma(max(每一列的不重複出現的字母))/len(str)即可
感覺很坑,不是求猜正確的概率,
而是求,隨機一個k,看一個首字母,存在能100%確定t的概率。
- Zabre 469 div2 C
比賽的時候沒看清楚題目,單個的0,和00 都是合法的,所以末尾剩下的0就不用考慮匹配了,這樣就很簡單
只要考慮1,充分利用現有的0,把1都給放了,(按照題意0101的規則放置)
這樣考慮,輸出-1的情況只有兩個
- 沒有0來放後面的1
- 最後放完了還有1在最後面
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <list>
#include <queue>
/*
#include <cmath>
#include <cstdlib>
#include <algorithm>
*/
#define mem(x) memset(x,0,sizeof(x))
using namespace std;
const int N=2e5+10;
int queue_tail[N];//儲存每一個子序列的尾部
vector<int> T[N];//儲存子序列的下標
int main()
{
string s;
cin>>s;
int len=0;
queue<int> zero,one;//暴力搜尋能放的位置會TLE,把能放的位置放在佇列裡
int top=0;//每個多出來的0都放在top位置,以便繼續放後面的1;
bool fg=true;
mem(queue_tail);
for(int i=0;i<s.length();i++)
{
char tp = s[i];
if(tp=='0')
{
int loc;
if(zero.empty())
{
loc=top++;
}
else
{
loc = zero.front();
zero.pop();
}
T[loc].push_back(i+1);
queue_tail[loc]=0;
one.push(loc);
}
else
{
int loc;
if(one.empty())
{
fg=false;
break;
}
else
{
loc = one.front();
one.pop();
}
T[loc].push_back(i+1);
queue_tail[loc]=1;
zero.push(loc);
}
}
for(int i=0;i<top;i++)
{
if(queue_tail[i]==1)
{
fg=false;
break;
}
}
if(fg)
{
int i=0;
printf("%d\n",top);
int len;
while(true)
{
len = T[i].size();
if(len==0)break;
printf("%d ",len);
for(int j=0;j<len;j++)
{
printf("%d%s",T[i][j],j==len-1?"\n":" ");
}
i++;
}
}
else
{
cout<<-1<<endl;
}
return 0;
}
E. Data Center Maintenance
題意:
題目很長讀了半天,就是幾個個東西
- 有n個數據中心
- 有m個客戶
- 每天有h小時
- 資料中心在某幾個小時點會進行一小時的維護
- 每個客戶能從兩個不同的資料中心獲取資料
- 問題的前提是任一客戶在任意時間都至少有一個數據中心不在維護
- 問題的輸出就是,關鍵 在保證6的情況下,取一個最小子集,將他們延後一個小時
- 試想,如果原來滿足6,那麼把所有維護機都往後延後一個小時,還是能保證6,所以每個問題都至少有一個解,就是全部拿來實驗
嘗試解題
資料n,m,h的範圍都是1e5;
nlogn的複雜度才能保證不TLE
對於第二個樣例(我們吧資料中心的下標列在客戶資料後面)
4 5 4
2 1 0 3
4 3 0 3
3 2 0 1
1 2 2 1
1 4 2 3
1 3 2 0
不難看出來,如果一個客戶的兩個客戶中心相差一個小時,那早更新的那個如果加入實驗,必然導致另外一個也加入實驗,對於一個二元關係我們可以建立一個圖來解決
例如第2個客戶,3 2
那麼資料中心3連一條有向弧到2;表示,如果資料中心3,如果加入試驗,那麼2也必須加入試驗。
那麼問題就轉換成為
求一個最小的連通塊
接下來程式碼就很好寫了
寫著發現沒這麼簡單
應該要先將這個有向圖縮點 成為一個DAG
然後 取 縮點後的所有出度為0的點 中 點的數目最小的那個連通塊,才能滿足題意。
縮點程式碼參考我的另外一篇blog
本題程式碼