[BJOI2019]奧術神杖(分數規劃,動態規劃,AC自動機)
阿新 • • 發佈:2019-04-21
tchar != ring -- esp fine true display sca
[BJOI2019]奧術神杖(分數規劃,動態規劃,AC自動機)
題面
洛谷
題解
首先乘法取\(log\)變加法,開\(c\)次根變成除\(c\)。
於是問題等價於最大化\(\displaystyle \frac{\sum val_i}{c}\)。典型的分數規劃的形式。
二分權值\(k\),每個點的點權變成\(val_i-k\),轉為求最值,那麽直接在\(AC\)自動機上\(dp\)就行了。
註意精度問題。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; #define MAX 1505 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } struct Node{int son[10],ff,s;double w;}t[MAX]; int tot; void Insert(char *s,int val) { int l=strlen(s+1),u=0; for(int i=1;i<=l;++i) { int c=s[i]-48; if(!t[u].son[c])t[u].son[c]=++tot; u=t[u].son[c]; } t[u].w=log(val);t[u].s+=1; } int Q[MAX],L,R; void BuildFail() { L=1; for(int i=0;i<10;++i)if(t[0].son[i])Q[++R]=t[0].son[i]; while(L<=R) { int u=Q[L++];t[u].w+=t[t[u].ff].w;t[u].s+=t[t[u].ff].s; for(int i=0;i<10;++i) if(t[u].son[i])t[t[u].son[i]].ff=t[t[u].ff].son[i],Q[++R]=t[u].son[i]; else t[u].son[i]=t[t[u].ff].son[i]; } } char T[MAX],S[MAX];int n,m; double f[MAX][MAX];int g1[MAX][MAX],g2[MAX][MAX]; void Tr(int i,int j,int k) { int v=t[j].son[k]; if(f[i][v]<f[i-1][j]+t[v].w) { f[i][v]=f[i-1][j]+t[v].w; g1[i][v]=j;g2[i][v]=k; } } bool check(double K) { for(int i=0;i<=tot;++i)t[i].w-=K*t[i].s; int len=strlen(T+1); for(int i=0;i<=len;++i) for(int j=0;j<=tot;++j)f[i][j]=-1e300; f[0][0]=0; for(int i=1;i<=len;++i) for(int j=0;j<=tot;++j) if(T[i]=='.')for(int k=0;k<10;++k)Tr(i,j,k); else Tr(i,j,T[i]-48); double ans=-1e300; for(int i=1;i<=tot;++i)ans=max(ans,f[len][i]); for(int i=0;i<=tot;++i)t[i].w+=K*t[i].s; return ans>0; } int main() { n=read();m=read(); scanf("%s",T+1); for(int i=1,v;i<=m;++i)scanf("%s",S+1),v=read(),Insert(S,v); BuildFail(); double l=0,r=21; while(r-l>1e-3) { double mid=(l+r)/2; if(check(mid))l=mid; else r=mid; } check(l);int pos=0,len=strlen(T+1); for(int i=1;i<=tot;++i)if(f[len][i]>f[len][pos])pos=i; for(int i=len;i;--i)S[i]=g2[i][pos]+48,pos=g1[i][pos]; for(int i=1;i<=len;++i)putchar(S[i]);puts(""); return 0; }
[BJOI2019]奧術神杖(分數規劃,動態規劃,AC自動機)