關於這個字首……是因為這個題目的名字很容易讓人想到一些別的題目,所以加字首區分一下。
設i表示a串不在lcs的位置,j表示b串不在lcs的位置。由於不能讓lcs結果為n,所以填入b串第j個位置的字元不能為s[j]。
我們先固定j,然後考慮b串的可能性個數。
考慮j<=i的情況。
第一條紅線前的部分就被確定了,只需要考慮第一條紅線後有多少種可能即可。
那麼就是看i取合法值時能造成的不同b串個數。
例如乙個i,假如存在乙個i′
<
i ,能造成相同的b串。
除兩條紅線以內的部分一定相同。現在紅線以內的部分也相同,那麼表明s[i』..i-1]=s[i』+1..i]也就是說s[i-1]=s[i]。
因此就很簡單了,合法的i一定滿足s[i-1]!=s[i]。然後列舉j並統計j之後有多少合法i,假設為t個,那麼此時貢獻為(m-1)*t,因為第j位有m-1種填法。
對於i<=j也同理,正著做一遍即可。然後減去i=j的情況。
**
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using
namespace
std;
typedef
long
long ll;
const
int maxn=100000+10;
char a[maxn];
int i,j,k,l,t,n,m,cnt;
ll ans;
char get()
int main()
cnt=1;
fo(i,1,n)
ans-=(ll)(m-1)*n;
printf("%lld\n",ans);
}
這個做法為什麼會*****的拿了0分呢?
因為*****做法沒有考慮到當j不同時也可能造出一樣的b串。
乙個簡單的反例是ab。
接下來,我們來定義一種合法的方案:
1、去掉a串第i位與b串第j位後完全相同。
2、s[i]!=s[i+1]。(這條性質是因為如果滿足這個會存在i』與i重複,具體看上乙個部分)
3、b串第j位填入的字元不為s[j]。
4、用(i,j)來表示這種方案,暫不考慮你在b串第j位到底填了什麼。
然後我們假設當前有兩種合法方案重了,分別為(i』,i)和(j』,j),對應的b串分別為b1和b2。讓我們來**一下吧。
為了方便,我們設i<=j。
接下來我們來證明一些東西,下面列舉的是不可能的結論。
1、j』>i。
那麼b2的第i位必然是s[i],而根據性質3我們知道b1的第i位必然不是s[i]。這種情況下b1與b2不可能相同。
同理我們知道了i′
>
j 也是不可能的。 2、i
i 所以紅線後的部分是s[i+1..n] 因為j
′<=
i<=
j 即j′
<=
j 所以橙線後的部分是s[j+1..n]
因為b1和b2要相同所以藍線後的部分相同。
有s[j..n]=b2第j位+s[j+1..n]
那麼b2第j位即為s[j],違背了性質3。 因此i
因此我們知道j′
,所以我們可以知道b1在藍線前的部分是s[1..i-1] 因為j
′<=
j ,所以我們可以知道b2在藍線前的部分是s[1..j』-1]+s[j』+1..i]
那麼我們知道s[1..i-1]=s[1..j』-1]+s[j』+1..i]
也就是s[j』..i-1]=s[j』+1..i]
那麼就有s[j』]=s[j』+1],這與性質2產生了矛盾。
因此我們得到了j』=i,同理有i』=j。
然後就得到了出現重時只能是哪種情況。
接著我們設(i,j)和與(j,i)重,現在就是**重的條件。設
然後我們知道b2的第i位為s[i+1],因為b1與b2相同,b1的第i位是s[i+1],而b1的第i位也不能是s[i]。當s[i]=s[i+1]時,b1第i位一定不是s[i],也就一定不是s[i+1],那麼b1與b2就不相同,所以不符合要求。因此s[i]要不等於s[i+1]。
然後,紅線前的部分和藍線後的部分肯定相同。
考慮橙線到紫線這一段相同,即s[i..j-2]=s[i+2..j]
那麼就有
s[i]=s[i+2]
s[i+1]=s[i+3]
…… s[j-2]=s[j]
因此我們得到(i,j)會被算重的條件:
1、s[i]!=s[i+1]
2、s[i..j]是乙個形如ababababa……這樣的字串。
因此,我們只需要預處理right[i]表示每次往後跳兩格,跳到的必須與本身相同跳到的最遠點。
列舉i,然後便可以得到最大j,那麼s[i..j]中任一字首都會被算重。
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using
namespace
std;
typedef
long
long ll;
const
int maxn=100000+10;
char a[maxn];
int right[maxn];
int i,j,k,l,t,n,m,cnt;
ll ans;
char get()
int main()
cnt=1;
fo(i,1,n)
ans-=(ll)(m-1)*n;
fo(i,1,n) right[i]=i;
fd(i,n-2,1)
if (a[i]==a[i+2]) right[i]=right[i+2];
fo(i,1,n-1)
printf("%lld\n",ans);
}
NOI2016 優秀的拆分
看到題目,資料範圍有點怪異。對於95 的資料,對於100 的資料,意思是只有5分是正解。好吧,95pts的 很明顯,答案就是 而如何才能拿到100pts呢?我們可以先列舉a段的長度,很明顯每個長度為lcp,與往後求lcs,若 這樣就可以通過 include include include inclu...
NOI2016 優秀的拆分
題目實際上要求我們求從每個點出發的aa串的數量 考慮點i的答案,發現如果字首i與字首j j i 的最長公共字尾 i j,那麼i點出發向前就存在乙個長度為i j的aa串,題目即求對於每個字首,有多少個在他之前的字首滿足條件 考慮字尾自動機,由於每個字首都是字尾自動機parent樹上的一點,即兩個字首的...
NOI2016 優秀的拆分
點此看題 首先轉化問題,我們可以求出a i b i a i b i a i b i 即以i ii結束 開始的aaaa aa串的數量,這樣答案就可以表示為 a i b i 1 sum a i times b i 1 a i b i 1 求這兩個陣列,可以隔距離len lenle n設定乙個點,這樣乙個...