【JZOJ3852】单词接龙
Description
Bsny从字典挑出N个单词,并设计了接龙游戏,只要一个单词的最后两个字母和另一个单词的前两个字母相同,那么这两个单词就可以有序的连接起来。
Bsny想要知道在所给的所有单词中能否按照上述方式接龙组成一个单词环(可能是多个),若能,求所有环的环中单词平均长度最大值。
Solution
我们把前两个字符或后两个字符看成点,单词看成边。
设
ans
为答案,那么
∑mi=1aim≥ans
∑mi=1ai≥ans⋅m
∑mi=1ai−ans≥0
然后我们二分答案,把所有边权减去 ans ,然后在图中找正权环即可。
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define rep(i,x) for(int i=ls[x];i;i=nx[i])
#define N 100010
#define SIZE 7010
#define eps 0.001
using namespace std;
int to[N],nx[N],ls[N],num=0;
double val[N];
int c[SIZE],tot=0;
char s[SIZE];
void link(int x,int y,double c)
{
num++;
to[num]=y;
nx[num]=ls[x];
ls[x]=num;
val[num]=c;
}
int g(char a,char b){
return (a-'a')*100+(b-'a');
}
int d[N],in[N];
double dis[N];
bool vis[SIZE],bz[SIZE];
int spfa(int qd,double z)
{
fo(i,0,tot) dis[i]=-2354343435434.00;
memset(vis,0,sizeof(vis));
memset(in,0,sizeof(in));
int l=0,r=1;
d[1]=qd;
vis[qd]=bz[qd]=1;
dis[qd]=0;
while(l<r)
{
l++;
int x=d[l];
rep(i,x)
{
int v=to[i];
if(dis[v]<dis[x]+val[i]-z)
{
dis[v]=dis[x]+val[i]-z;
if(!vis[v])
{
in[v]++;
if(in[v]>tot) return 1;
vis[v]=bz[v]=1;
d[++r]=v;
}
}
}
vis[x]=0;
}
return 0;
}
bool check(double mid)
{
memset(bz,0,sizeof(bz));
fo(i,1,tot)
if(!bz[i])
if(spfa(i,mid)>0) return 1;
return 0;
}
int main()
{
int n;
scanf("%d",&n);
int mx=0;
fo(i,1,n)
{
scanf("%s",s+1);
int l=strlen(s+1);
int t1=g(s[1],s[2]),t2=g(s[l-1],s[l]);
if(!c[t1]) c[t1]=++tot;
if(!c[t2]) c[t2]=++tot;
link(c[t1],c[t2],l*1.0);
mx=max(mx,l);
}
printf("%d\n",check(883.87));
double l=0,r=mx*1.0;
while(l+eps<r)
{
double mid=(l+r)/2;
bool t=check(mid);
if(t) l=mid;
else r=mid;
}
printf("%.2lf",l);
}