題意的話就看題面吧。
我們一步一步的來分析:
首先吃最少的泡椒,那麼顯然可以貪心,由於n×n
n\times n
n×n貢獻的肯定比後面的方式都大,所以我們考慮將乙個串它的所有存在的字尾串全部先放在前面,這時就不會用第一種了,然後我們考慮,可以將這種關係用邊連起來,就成了一棵樹,我們可以舉個例子來看:
5ab
babb
bba
這個例子便可以連出這樣的一張圖:
我們發現,如果先放a
aa這邊的,那麼最後就會是:
a - 1
ba - 1
bba - 1
b - 4
bb - 1
cost = 8
但是我們明顯可以發現如果先放b
bb這邊的,那麼當到a
aa時,它只會有3
33的代價,而其他都是1
11的代價,所以如下這樣會更優秀:
b - 1
bb - 1
a - 3
ba - 1
bba - 1
cost = 7
那麼貪心的考慮,也就是這棵樹上,我們按照子樹大小,從小到大的遍歷會更優秀一些。
所以一種方法是用hash演算法,在o(n
2)o(n^2)
o(n2
)的時間內建出這個樹,然後遍歷一遍排個序就可以出答案了。
目前瓶頸就在於建樹,我們可以發現它的每個都是和字尾有關,但是直接建一棵廣義字尾自動機太麻煩了,所以我們將其翻轉後,字尾就變成了字首,那麼只需插入到一棵trie(字典)樹中即可,對於乙個串是另乙個串的字首,那麼在字典樹上它就是另乙個的祖先(在另乙個同一條鏈的上面),然後遍歷一遍即可建出原樹。
複雜度變成o(∣
len∣
∗26+n
)o(|len|*26+n)
o(∣len
∣∗26
+n)就可以過了。
**:
#include
#include
#include
#include
#define ll long long
using
namespace std;
const
int n=
1e5+
5,m=
6e5+1;
int n,bef[n]
;char str[m]
;ll ans;
int son[m][26
],f[n]
,flag[m]
,sze[n]
,bel[m]
,tot=1;
struct ssss(
int a,
int b):to
(a),
last
(b)}g[m]
;int head[n]
,cnt;
void
add(
int a,
int b)
void
addin
(char
*s,int k)
bel[now]
=k;//建trie樹,記錄是哪個字串
}struct node
node
(int a,
int b)
:sze
(a),
id(b)
bool
operator
<
(node a)
const
//按子樹大小排序};
vector vec[n]
;void
dfs(
int a,
int fa)
//建樹
}void
find
(int a)
sort
(vec[a]
.begin()
,vec[a]
.end()
);//排序
}int rak;
void
get(
int a)
void
file()
void
close()
intmain()
dfs(1,
0);find(0
);get(0)
;for
(int i=
1;i<=n;i++
) ans+
=1ll
*(bef[i]
-bef[f[i]])
;//統計答案
printf
("%lld\n"
,ans)
;close()
;return0;
}
Scoi2016 背單詞(trie 貪心)
題意 重新解釋一下題意吧 題意晦澀難懂 給定n個單詞,你可以按照順序學習,當學習這一單詞時,這個單詞是第x個要學習的單詞,需要的代價分三類 1 若存在其他單詞是其字尾沒被學習,則代價為n2 2 若不存在其他單詞是其字尾,則代價是x 3 否則代價是x y y是最靠後的是其字尾的單詞學習的位置 題解 首...
SCOI2016 背單詞 Trie樹,貪心
題目鏈結 首先吐槽一波原題意,描述地太不清楚了,還是出題人想要出語文斷句題?題目鏈結是團隊考試的題,題意重置版。顯然我們要避免那種 i i 的情況,因為這樣非常不划算,i 2 i i j 那我們來看看能不能安排乙個合法的順序來規避這個情況。顯然是可以的,因為如果我們按照字尾關係連邊,它會形成乙個 d...
洛谷P3294 SCOI2016 背單詞 題解
題目傳送 閱讀理解題題意解釋可以看這位大佬的部落格。發現求字尾與倒序求字首是等價的,而找字首自然就想到了trie樹。將所有字串翻轉後再建入trie樹中,再對每乙個字串翻轉後從trie樹中找字首,就能找到乙個字串的所有字尾了。由第三種情況知我們要想最小化總代價,則最小化乙個字串與最靠近它的字尾間的距離...