[BJOI2019]奥术神杖

匿名 (未验证) 提交于 2019-12-02 23:26:52

题目

我要是生在\(bj\)估计就凉了,一道\(d1t1\)做了\(4h+\)

首先看到这个鬼畜的计算贡献的柿子

\[\prod_{i=1}^na_i^{\frac{b_i}{\sum_{j=1}^nb_j}}\]

\(a_i\)为第\(i\)个字符串的价值,\(b_i\)为第\(i\)个字符串出现的次数

我们显然需要取一个\(\ln\)

就变成了

\[\sum_{i=1}^n\frac{b_i}{\sum_{j=1}^nb_j}\ln a_i\]

这样写太难看了

我们随便一些就变成了

\[\frac{\sum_{i=1}^nb_i\ln a_i}{\sum_{i=1}^nb_i}\]

一看这不分数规划吗,我们直接二分一个\(mid\)看看答案是否能更大就好了

如果更大

\[\frac{\sum_{i=1}^nb_i\ln a_i}{\sum_{i=1}^nb_i}>mid\]

也就是

\[\sum_{i=1}^nb_i(\ln a_i-mid)>0\]

显然这又是一个多串匹配的问题,我们直接上\(AC\)自动机

现在的问题就是在自动机上找到一条长度为\(n\)的路径,点权和最大

直接在自动机上按位\(dp\)就好了,\(dp[i][j]\)表示匹配的长度是\(i\)当前在自动机上的第\(j\)个节点的最大路径长度

代码

#include<queue> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define re register #define LL long long #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) const double eps=1e-12; const int maxn=1505; struct E{int v,nxt;}e[maxn]; int n,m,cnt,num,L; char S[maxn],T[maxn]; int id[maxn],fa[maxn],vis[2][maxn],son[maxn][10]; int r[maxn][maxn],c[maxn][maxn],head[maxn],tmp[maxn]; double val[maxn],d[maxn]; double dp[2][maxn],f[maxn][maxn]; inline void add(int x,int y) {     e[++num].v=y;e[num].nxt=head[x];head[x]=num; } void Dfs(int x) {     for(re int i=head[x];i;i=e[i].nxt)         d[e[i].v]+=d[x],Dfs(e[i].v); } inline int pd(double a) {return a+eps>0&&a-eps<0;} inline void ins(int j) {     int v;     scanf("%s",T+1);scanf("%d",&v);     int now=0,len=strlen(T+1);L+=len;     for(re int i=1;i<=len;i++) {         if(!son[now][T[i]-'0']) son[now][T[i]-'0']=++cnt;         now=son[now][T[i]-'0'];     }     tmp[now]++;id[j]=now;val[j]=log(v); } inline void Build() {     std::queue<int> q;     for(re int i=0;i<=9;i++) if(son[0][i]) q.push(son[0][i]);     while(!q.empty()) {         int k=q.front();q.pop();         add(fa[k],k);tmp[k]+=tmp[fa[k]];         for(re int i=0;i<=9;i++)         if(son[k][i]) fa[son[k][i]]=son[fa[k]][i],q.push(son[k][i]);             else son[k][i]=son[fa[k]][i];     } } inline void Pre_work(double mid) {     for(re int i=0;i<=cnt;i++) d[i]=0;     for(re int i=1;i<=m;i++) d[id[i]]+=val[i]-mid;     Dfs(0); } inline int check(double mid) {     Pre_work(mid);     memset(dp,-20,sizeof(dp));     dp[0][0]=0;int o=0;vis[0][0]=1;     for(re int i=0;i<n;i++,o^=1)  {         memset(dp[o^1],-20,sizeof(dp[o^1]));         memset(vis[o^1],0,sizeof(vis[o^1]));         for(re int j=0;j<=cnt;j++) {             if(!vis[o][j]) continue;             if(S[i+1]>='0'&&S[i+1]<='9') {                 int v=son[j][S[i+1]-'0'];                 vis[o^1][v]=1;                 dp[o^1][v]=max(dp[o^1][v],dp[o][j]+d[v]);                 continue;             }             for(re int k=0;k<=9;k++) {                 int v=son[j][k];                 vis[o^1][v]=1;                 dp[o^1][v]=max(dp[o^1][v],dp[o][j]+d[v]);             }         }     }     for(re int i=0;i<=cnt;i++)      if(dp[o][i]>0&&!pd(dp[o][1]))          return 1;     return 0; } void dfs(int len,int now) {     if(!len) return;     dfs(len-1,c[len][now]);     putchar(r[len][now]+'0'); } inline void solve(double mid) {     Pre_work(mid);     memset(f,-20,sizeof(f));     f[0][0]=0;     for(re int i=0;i<n;i++)          for(re int j=0;j<=cnt;j++) {             if(S[i+1]>='0'&&S[i+1]<='9') {                 int v=son[j][S[i+1]-'0'];                 if(f[i][j]+d[v]>f[i+1][v]) {                     c[i+1][v]=j;r[i+1][v]=S[i+1]-'0';                     f[i+1][v]=f[i][j]+d[v];                 }                 continue;             }             for(re int k=0;k<=9;k++) {                 int v=son[j][k];                 if(f[i][j]+d[v]>f[i+1][v]) {                     c[i+1][v]=j,r[i+1][v]=k;                     f[i+1][v]=f[i][j]+d[v];                 }             }         }     int t=0;     for(re int i=1;i<=cnt;i++)     if(f[n][i]>f[n][t]) t=i;     dfs(n,t); } namespace sub1 {     char g[15];     double ans=0;     void dfs(int x) {         if(x==n+1) {             double w=0;int y=0,now=0;             for(re int i=1;i<=n;i++) {                 now=son[now][S[i]-'0'];                 y+=tmp[now];w+=d[now];             }             if(w/(1.0*y)>ans) {                 ans=w/(1.0*y);                 for(re int i=1;i<=n;i++) g[i]=S[i];             }             return;         }          if(S[x]!='.') {             dfs(x+1);             return;         }         for(re int i=0;i<10;i++) {             S[x]=i+48;             dfs(x+1);         }         S[x]='.';              }     inline void solve() {         Pre_work(0);         dfs(1);         for(re int i=1;i<=n;i++) putchar(g[i]);     } } int main() {     scanf("%d%d",&n,&m);     scanf("%s",S+1);     for(re int i=1;i<=m;i++) ins(i);     Build();     if(n<=6&&L<=20) {         sub1::solve();         return 0;     }     double l=0,r=1e5+5,ans=0;     while(r-l>eps) {         double mid=(l+r)/2.0;         if(check(mid)) l=mid,ans=mid;             else r=mid;     }     solve(ans);     return 0; }   
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!