【BJOI 2019】奧術神杖

題意

你有一個長度為 $n$ 的模板串(由 $0-9$ 這 $10$ 個數字和通配符 $.$ 組成),還有 $m$ 個匹配串(只由 $0-9$ 這 $10$ 個數字組成),每個匹配串有一個魔力值 $v_i$。你要把模板串的每個 $.$ 都換成一個數字,使得模板串的魔力值最大。模板串的魔力值定義為:模板串中每出現一次任意一個匹配串 $s_i$,字符串的魔力就 $\times v_i$。最終魔力值開 $c$ 次方根,$c$ 為模板串中出現的匹配串的總數。

$1\le n,m,s\le 1501,\space 1\le v_i\le 10^9$

題解

王·能過就行·子健

顯然只要三個 $10^9$ 大小的數乘起來就爆 $long\space long$ 了(即 $\prod v_i$ 會很大),而高精度開根既難寫又爆復雜度(光乘法就爆時間了),所以不能直接按題目的公式求。

如果你沒學過數學(比如我),可以把所有 $v_i$ 各自開 $c$ 次方根再相乘,但即使開 $long\space double$ 也會爆精度,不過可以拿 $80$ 分。

如果你學過數學,應該記得高一數學必修 $1$ 中有一章講了關于 $log$ 的各種性質,其中有兩條是

$$\log_a{MN} = \log_a{M}+\log_a{N}$$

$$\log_a{N}^k = k\times \log_a{N}$$

其中 $a$ 可以是任意實底數。

第一條式子中的 $MN$ 可以拓展成任意多個乘數,等號右邊就會得到一堆 $log$ 值相加。簡單地說就是因為冪值相乘等于指數相加(比如 $2^4$ 變成 $2^5$ 次方,值乘了 $2$,但指數只加了 $1$)。

具體證明可以去翻書。

?

把兩個公式組合一下,就可以推這題的公式

$$ans = \sqrt[c]{v_1\times v_2\times ...\times v_k} = (v_1\times v_2\times ...\times v_k)^{\frac{1}{c}}$$

兩邊同時取以一個實數 $a$ 為底的對數,得到

$$\log_a{ans} = \log_a{(v_1\times v_2\times ...\times v_k)^{\frac{1}{c}}}$$

$$\log_a{ans} = \frac{1}{c}\times \log_a{(v_1\times v_2\times ...\times v_k)}$$

$$\log_a{ans} = \frac{1}{c}(\log_a{v_1}+\log_a{v_2}+...+\log_a{v_k})$$

因為這題只需要你求方案,所以你只要確保不同方案之間的相對魔力值即可,不用維護具體的 $ans$ 值,所以可以把 $ans$ 取 $log$,$log$ 的底數 $a$ 也可以隨便取,大部分人應該都取的是自然對數 $e$

?

不難發現等號右邊變成了一個類似于平均數的東西,仔細觀察即可發現,把所有匹配串的魔力值 $v_i$ 取 $ln$ 后,你要使出現的所有匹配串的 $v_i$ 的平均數最大。

平均數最大這種東西就是套路的01分數規劃……

具體做法就是,二分平均數 $x$,然后把所有匹配串的 $a_i$ 都減去 $x$,問題就變成了如何使 $v_i$ 之和最大。在所有模板串組成的 AC 自動機上 $dp$ 即可。

AC 自動機上 $dp$ 的狀態就是 $f_{i,j}$ 表示確定模板串的前 $i$ 位,按模板串的前 $i$ 位跑 AC 自動機到達的點的編號為 $j$ 時,模板串的魔力值最大是多少。

然后判斷一下模板串的第 $i$ 位是不是通配符就行了,是的話就可以往任意兒子轉移,不是的話就要沿對應的字符邊轉移。

時間復雜度 $O(10ns\log{\frac{\ln v_{max}}{eps}})$。

這他嗎什么復雜度,怎么跑過的……能過就行了

  1 #include<bits/stdc++.h>
  2 #define N 1505
  3 #define inf 1e99
  4 #define eps 1e-6
  5 using namespace std;
  6 inline int read(){
  7     int x=0; bool f=1; char c=getchar();
  8     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
  9     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
 10     if(f) return x;
 11     return 0;
 12 }
 13 int n,m;
 14 char T[N];
 15 namespace AC{
 16     int cnt,ch[N][12],sum[N]; double val[N];
 17     inline void ins(char *s,double v){
 18         int u=0,len=strlen(s),c;
 19         for(int i=0;i<len;++i){
 20             c=s[i]-'0';
 21             if(!ch[u][c]) ch[u][c]=++cnt;
 22             u=ch[u][c];
 23         }
 24         ++sum[u], val[u]+=v;
 25     }
 26     int que[N],l,r,fail[N];
 27     void BuildAC(){
 28         fail[0]=-1, que[l=r=1]=0;
 29         while(l<=r){
 30             int u=que[l++];
 31             for(int i=0;i<10;++i)
 32                 if(!ch[u][i]) ch[u][i]=ch[fail[u]][i];
 33                 else fail[ch[u][i]]=ch[fail[u]][i], que[++r]=ch[u][i];
 34         }
 35         for(int i=2;i<=r;++i){
 36             sum[que[i]]+=sum[fail[que[i]]];
 37             val[que[i]]+=val[fail[que[i]]];
 38             //cout<<que[i]<<' '<<fail[que[i]]<<' '<<sum[que[i]]<<' '<<val[que[i]]<<endl;
 39         }
 40     }
 41     
 42     double f[N][N]; int g[N][N][2]; char ansStr[N];
 43     double DP(double x){
 44         //cout<<x<<endl;
 45         for(int j=0;j<=cnt;++j) val[j]-=sum[j]*x;
 46         for(int i=0;i<=n;++i)
 47             for(int j=0;j<=cnt;++j)
 48                 f[i][j]=-inf;
 49         f[0][0]=0;
 50         for(int i=0;i<n;++i){
 51             for(int j=0;j<=cnt;++j){
 52                 if(f[i][j]==-inf) continue;
 53                 if(T[i]=='.'){
 54                     for(int k=0;k<10;++k){
 55                         int _j=ch[j][k];
 56                         if(f[i+1][_j]<f[i][j]+val[_j]){
 57                             f[i+1][_j]=f[i][j]+val[_j];
 58                         }
 59                     }
 60                 }
 61                 else{
 62                     int k=T[i]-'0', _j=ch[j][k];
 63                     if(f[i+1][_j]<f[i][j]+val[_j]) f[i+1][_j]=f[i][j]+val[_j];
 64                 }
 65             }
 66         }
 67         for(int i=0;i<=cnt;++i) val[i]+=sum[i]*x;
 68         int ans=0;
 69         for(int j=1;j<=cnt;++j) if(f[n][j]>f[n][ans]) ans=j;
 70         //cout<<f[n][ans]<<endl;
 71         return f[n][ans];
 72     }
 73     void _DP(double x){
 74         for(int j=0;j<=cnt;++j) val[j]-=sum[j]*x;
 75         for(int i=0;i<=n;++i)
 76             for(int j=0;j<=cnt;++j)
 77                 f[i][j]=-inf;
 78         f[0][0]=0;
 79         for(int i=0;i<n;++i){
 80             for(int j=0;j<=cnt;++j){
 81                 if(f[i][j]==-inf) continue;
 82                 if(T[i]=='.'){
 83                     for(int k=0;k<10;++k){
 84                         int _j=ch[j][k];
 85                         if(f[i+1][_j]<f[i][j]+val[_j]){
 86                             f[i+1][_j]=f[i][j]+val[_j],
 87                             g[i+1][_j][0]=j, g[i+1][_j][1]=k;
 88                         }
 89                     }
 90                 }
 91                 else{
 92                     int k=T[i]-'0', _j=ch[j][k];
 93                     if(f[i+1][_j]<f[i][j]+val[_j])
 94                         f[i+1][_j]=f[i][j]+val[_j],
 95                         g[i+1][_j][0]=j, g[i+1][_j][1]=k;
 96                 }
 97             }
 98         }
 99         for(int i=0;i<=cnt;++i) val[i]+=sum[i]*x;
100         int ans=0;
101         for(int j=1;j<=cnt;++j) if(f[n][j]>f[n][ans]) ans=j;
102         for(int i=n;i>0;--i){
103             ansStr[i-1]=g[i][ans][1]+'0';
104             ans=g[i][ans][0];
105         }
106     }
107 }
108 using namespace AC;
109 int main(){
110     //freopen("1.in","r",stdin);
111     //freopen("1.out","w",stdout);
112     n=read(), m=read(); scanf("%s",T);
113     char S[N]; double V;
114     for(int i=1;i<=m;++i){
115         scanf("%s%lf",S,&V);
116         ins(S,log(V));
117     }
118     BuildAC();
119     double l=0, r=log(1e9+1), mid, ans=0;
120     while(r-l>eps){
121         mid=(l+r)/2;
122         if(DP(mid)>0) ans=mid, l=mid;
123         else r=mid;
124     }
125     //cout<<ans<<endl;
126     _DP(ans);
127     printf("%s\n",ansStr);
128     return 0;
129 }
Viev Code

總結:

1. 這類題不能直接 $dp$ 求最大平均數。因為求最大平均數這種問題,除了分數規劃外(即二分答案),只能在某些情況下用貪心(比如從大到小取)。

? ? 若不能貪心,我們不能把上述 $dp$ 的值直接記為最大平均數 或者同時記一個最小的匹配數量。考慮平均數這個東西的本質,對于到達同一狀態的兩種情況,可能一種情況匹配的數少,平均數也更小;但把兩種情況同時加入一個新數,這種情況的新平均數就可能比另一種情況的新平均數大了。比如兩個數集 $\{10\}$ 和 $\{9,9,9,14\}$,前者的平均數是 $10$,后者的平均數是 $10.25$;但把兩個數集同時加入一個數 $11$,前者的平均數變成了 $10.5$,后者的平均數變成了 $10.4$。所以如果用 $dp$ 求最大平均數,必須再開一維狀態記匹配的串數(即要求多少個數的平均數),但匹配的串數可能很多,再開一維狀態的話時空復雜度都不能承受。所以只能分數規劃。

2. 這道題告訴我們一定要學好數學這門文化課,否則會被數學殺。

轉載于:https://www.cnblogs.com/scx2015noip-as-php/p/bjoi2019d1t1.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/386814.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/386814.shtml
英文地址,請注明出處:http://en.pswp.cn/news/386814.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

keras模型中的默認初始化權重

權重的初始化&#xff0c;決定了模型訓練的起點。一個良好的初始化可以加快訓練過程&#xff0c;同時避免模型收斂至局部最小值。為了在訓練過程中避免使得權重的變化總沿著同一個方向&#xff0c;我們盡量避免將所有權重都初始化為同一個值&#xff0c;如全0矩陣或全1矩陣。 …

java oracle的枚舉錯誤

public enum OracleErrorTypeEnum implements BaseEnum {ORA00001("ORA-00001","不允許有重復的數據"),ORA00017("ORA-00017","請求會話以設置跟蹤事件"),ORA00018("ORA-00018","超出最大會話數"),ORA00019(&quo…

C# 篇基礎知識10——多線程

1.線程的概念 單核CPU的計算機中&#xff0c;一個時刻只能執行一條指令&#xff0c;操作系統以“時間片輪轉”的方式實現多個程序“同時”運行。操作系統以進程&#xff08;Process&#xff09;的方式運行應用程序&#xff0c;進程不但包括應用程序的指令流&#xff0c;也包括運…

keras中mean square error均方誤差理解

機器學習中&#xff0c;針對不同的問題選用不同的損失函數非常重要&#xff0c;而均方誤差就是最基本&#xff0c;也是在解決回歸問題時最常用的損失函數。本文就keras模塊均方誤差的計算梳理了一些細節。 首先看一下均方誤差的數學定義 : 均方誤差是預測向量與真實向量差值的…

Java并發Semaphore信號量的學習

public class MyThreadTest {private final static Semaphore semaphore new Semaphore(2);// 設置2個車位public static void main(String[] args) {System.out.println("start");p(semaphore, true, 1);p(semaphore, false, 2);p(semaphore, false, 3);p(semaphor…

快速理解binary cross entropy 二元交叉熵

Binary cross entropy 二元交叉熵是二分類問題中常用的一個Loss損失函數&#xff0c;在常見的機器學習模塊中都有實現。本文就二元交叉熵這個損失函數的原理&#xff0c;簡單地進行解釋。 首先是二元交叉熵的公式 : Loss?1N∑i1Nyi?log?(p(yi))(1?yi)?log(1?p(yi))Loss …

Docker搭建自己的GitLab

Docker搭建自己的GitLab docker 介紹 **GitLab: ** GitLab 是一個用于倉庫管理系統的開源項目&#xff0c;使用Git作為代碼管理工具&#xff0c;并在此基礎上搭建起來的web服務 **Docker: ** Docker 是一個開源的應用容器引擎&#xff0c;讓開發者可以打包他們的應用以及依賴…

kolla-ansible-----常用命令

常用命令 kolla-ansible prechecks -i multinode #部署前環境檢測 kolla-genpwd #生成/etc/kolla/password.yml密碼配置文件 kolla-ansible post-deploy -i multinode #生成認證文件 kolla-ansible mariadb_recovery -i /opt/mutinode #恢復數據庫 kolla-ansible -i multi…

python numpy 分離與合并復數矩陣實部虛部的方法

在進行數字信號處理的過程中&#xff0c;我們往往有對短時傅里葉變換頻譜(spectrogram)進行分析的需求。常見的分析手段對應歐拉公式分為兩種&#xff0c;要么使用模與相位的形式&#xff0c;要么使用實部虛部。本文分享一個簡單的將復數光譜圖分解為實部與虛部以及將兩個部分重…

flowable 任務節點多實例使用

我們在使用Flowable 工作流引擎的時候&#xff0c;最常用的肯定是任務節點&#xff0c;因為在OA系統、審批系統、辦公自動化系統中核心的處理就是流程的運轉&#xff0c;在流程運轉的時候&#xff0c;可能我們有這樣的一個需求&#xff0c;在一個任務節點的時候&#xff0c;我們…

LeetCode Range Sum Query Immutable

2131231轉載于:https://www.cnblogs.com/ZHONGZHENHUA/p/10873807.html

如何使用python導入mat格式的數據并整理

mat格式是一般而言的matlab數據的存儲格式&#xff0c;對于經常要混用matlab和python的數據處理相關的問題&#xff0c;我們往往需要將matlab中的數據導入至python&#xff0c;本文給出了相關的方法。 from scipy.io import loadmat import numpy as npdict_mat loadmat(&quo…

Java靜態類使用 使用 service

Springboot中如果希望在Utils工具類中&#xff0c;使用到我們已經定義過的Dao層或者Service層Bean&#xff0c;可以如下編寫Utils類&#xff1a; 1. 使用Component注解標記工具類StatisticsUtils&#xff1a; 2. 使用Autowired(Autowired和Resource的區別不再介紹)注入我們需…

Codeforces 1144D Deduction Queries 并查集

Deduction Queries 用并查集維護前綴的關系&#xff0c; 在同一個聯通塊內兩兩之間的異或值都是已知的。 每個點再維護一個和它當前父親的異或值&#xff0c; 壓縮路徑的時候更新一下就好了。 #include<bits/stdc.h> #define LL long long #define LD long double #defin…

python一步將npy數據保存成mat

import scipy.io as io io.savemat("dataname.mat", {data: npy_data})使用scipy庫中的io模塊&#xff0c;只需一步就可以將npy矩陣保存為mat格式文件&#xff0c;第一個參數是保存路徑&#xff0c;第二個參數需要輸入一個字典&#xff0c;data鍵對應需要保存的數據。…

docker oracle 11g

話不多說 開始記錄docker拉取阿里的oracle11g 鏡像并進行配置&#xff0c; 用pl/sql 可以登錄為最終結果 navicat連接是在最后一步 這是我們所需要進行拉取oracle鏡像的樓主所給出的說明 參考&#xff1a;https://blog.csdn.net/zwx521515/article/details/77982884 但是根…

Linux的目錄結構

Linux文件系統是呈樹形結構&#xff0c;了解Linux文件系統的目錄結構&#xff0c;對于我們駕馭Linux還是有必要的。 目錄 說明 / Linux文件系統的入口&#xff0c;也是處于最高一級的目錄 /bin 基本系統所需要的命令。功能和/usr/bin類似&#xff0c;這個目錄中的文件都是…

npy一維數組如何對給出的索引進行反選

本文主要解釋了如何根據給定的索引對一維數組進行反選的操作。 以下文數據為例 import numpy as np data np.array([ 0.93825827, 0.26701143, 0.99121108, 0.35582816, 0.90154837, 0.86254049, 0.83149103, 0.42222948, 0.27309625, 0.38925281] )如果我們給定一個閾值…

一文看懂卷積神經網絡CNN的核心

在之前&#xff0c;我總結了關于計算機神經網絡與梯度下降的核心&#xff0c;詳見下文鏈接 : 一文看懂計算機神經網絡與梯度下降 本文主要會對圖像相關的機器學習中最為重要的網絡&#xff0c;卷積神經網絡作個人的理解分析。 1. 為什么要使用卷積神經網絡 在講述原理之前&am…

[LeetCode] Two Sum

一刷&#xff1a; import java.util.Arrays;public class Solution1 { public int[] twoSum(int[] nums, int target) {int[] indexnew int[2];int sum0;for (int i 0; i < nums.length; i) {for (int j i1; j < nums.length; j) {sumnums[i]nums[j];index[0] i;index[…