首页 > 代码库 > BZOJ4567: [Scoi2016]背单词

BZOJ4567: [Scoi2016]背单词

题面原文大致如下:

有n个单词,从小到大填入表,对于序号为x的单词(1~x-1都已被填入):

  1. 如果存在一个单词是它的后缀,并且当前没有被填入表内,代价为n*n;
  2. 当它的所有后缀都被填入表内的情况下,如果在1~x-1的位置上的单词都不是它的后缀,代价为x;
  3. 当它的所有后缀都被填入表内的情况下,如果1~x-1的位置上存在是它后缀的单词,所有是它后缀的单词中,序号最大为y,代价为x-y。

求最小代价。

写题面的人脑子打了结吧能写出这种见鬼的描述,逻辑被狗吃了。

第二个条件相当于说:当它的所有后缀都被填入表内的情况下,表内的单词都不是它的后缀。

这句子是人写出来的吗。

然后你必须推导出这句话的意思是:“它的所有后缀”为空集。

而“它的所有后缀”是指是它的后缀的所有单词。

最后把这个题面翻译成人话就是:

  1. 如果存在一个单词是它的后缀,且当前没被填入,代价为n*n;
  2. 如果不存在一个单词是它的后缀,代价为x;
  3. 如果存在一个单词是它的后缀,且已填入的是它后缀的单词中序号最大的为y,代价为x-y。

然后就是个sb贪心。

我还能说什么呢。

只有我一个人读不懂题吗,真是惨无人道。

#include<bits/stdc++.h>
#define S t[u][z[j]-97]
#define I t[u][i]
#define pb push_back
#define RAN(v)v.begin(),v.end()
#define FOR(i,v)	for(typeof(v.end())i=v.begin();i!=v.end();++i)
using namespace std;
typedef long long ll;
const int N=100005;
const int M=510005;
int n,tot;
char z[M];
int t[M][26],l[M],r[N];
ll f[N];
vector<int>e[N];
bool foo(int i,int j){
	return r[i]<r[j];
}
void dfs1(int u,int v){
	if(l[u])e[v].pb(l[u]);
	for(int i=0;i<26;++i)
		if(I)dfs1(I,l[u]?l[u]:v);
}
void dfs2(int u){
	r[u]=1;
	FOR(i,e[u])
		dfs2(*i),r[u]+=r[*i];
	sort(RAN(e[u]),foo);
	f[u]=1;
	int s=0;
	FOR(i,e[u])
		f[u]+=f[*i]+s,s+=r[*i];
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%s",z);
		int u=0;
		for(int j=strlen(z)-1;~j;--j)
			u=S?S:S=++tot;
		l[u]=i;
	}
	dfs1(0,0),dfs2(0);
	printf("%lld\n",f[0]-1);
}

  

 

BZOJ4567: [Scoi2016]背单词