题目链接
大致题意
给你 (n) 个字符串, 要求你给出最小的代价.
对于每个字符串:
1.如果它的后缀在它之后,那么代价为 (n^2).
2.如果一个字符串没有后缀,那么代价为 (x), (x)是它所处的位置.
3.如果字符串前面有它的后缀且位置为 (y),那么代价为 (x-y).
Solution
比较鬼畜的体面,出题人语文水平亟需提高...
-
反向建立一棵 Trie 树.
-
根据 Trie 树对于每一个被标记过的点重新连边.
-
考虑贪心
条件1 有它一定不是最优的,显然后面两种贡献最多也才(n) .令每一个点为 (rt), 子节点为 (u).(siz)为以其为后缀的字符串个数.
题意很明显要找一种最优的 dfs 序.
使得 (sum{id[u]-id[rt]}) 最小.
1.很明显有后缀的我们先走其后缀再走它自己
2.可以证明每次先走 (siz) 小的点总是最优的.简单证明:
令现在有一个点为(x),且其有两个子节点(u_1,u_2),(siz)分别为(siz_1,siz_2).
且满足 (siz_1>siz_2).
如果先走(1)节点,那么代价为:
(id[x]+siz[u_1]*2+siz[u_2])
这个式子,可以自己根据题意自己理解一下.
因为很明显我们要先走完(1)才能走(2).
同理先走(2)节点代价为 (id[x]+siz[u_2]*2+siz[u_1]).
很明显 先走 (2) 代价小于先走 (1).
### Code ```cpp #include
{
a[++size].to=y;
a[size].next=head[x];
head[x]=size;
}
void insert(int x,char *s)
{
int len=strlen(s),u=0,lst=0;
for(int i=len-1;i>=0;i--)
{
int p=s[i]-'a';
if(!ch[u][p])ch[u][p]=++tot;
u=ch[u][p];
}
pd[u]=x;
return;
}
void pre(int x,string s)
{
int u=0,lst=0,len=s.length();
for(int i=len-1;i>=0;i--)
{int p=s[i]-'a';
u=ch[u][p];if(pd[u]&&i!=0)lst=pd[u];}
add(lst,x);
}
void dfs(int x)
{
t[x].x=x;t[x].siz=1;
for(int i=head[x];i;i=a[i].next)
{
int tt=a[i].to;
dfs(tt);
t[x].siz+=t[tt].siz;
}
}
void getans(int x)
{
priority_queue
for(int i=head[x];i;i=a[i].next)
{int tt=a[i].to;q.push(t[tt]);}
while(q.empty()!=1)
{
pp now=q.top();
id[now.x]=++tot;
getans(now.x);
ans+=id[now.x]-id[x];
q.pop();
}
}
char s[maxn];
string c[maxm];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%s",s);
insert(i,s);
int len=strlen(s);
for(int j=0;j<len;j++)
{c[i]+=s[j];}
}
for(int i=1;i<=n;i++)
pre(i,c[i]);
tot=0;
dfs(0);
getans(0);
cout<<ans<<endl;
}