首页 > 其他分享 >Qoj 9111 Zayin ans String / ABC 356 E

Qoj 9111 Zayin ans String / ABC 356 E

时间:2024-09-06 15:53:07浏览次数:6  
标签:ABC String 9111 sum tr tp son int ans

Qoj 9111 Zayin ans String / ABC 356 E

谨以此帖记录一个有意思的 Trick

题意

给了一个长度为 \(n\) 的目标串 \(s\) 和 \(m\) 个模式串

每个模式串有一个价值 \(v\)

要求从 \(s\) 中选出一个子序列 \(t\), 定义 \(t\) 的价值为他的所有子串的价值和(若该子串没出现在模式串中, 那么他的价值为 0)

最大化 \(t\) 的价值和长度的比值

思路

看到多模匹配问题, 想到 AC 自动机

考虑把所有模式串加到 AC 自动机里面, 然后在自动机上 dp

自然地想到 \(dp(i, j, k)\) 表示现在在 \(s\) 的第 \(i\) 位, 选了 \(j\) 位, 这一位在自动机的 \(k\) 节点上

转移就枚举选的下一位是谁

时间复杂度 \(O(n^4)\), 不可接受

现在该 Trick 救场了

难点在于我们无法直接比较一个选了 \(i\) 位, 一个选了 \(j\) 位的两个子序列的价值, 所以必须记录他们的长度

现在介绍一种方法

二分答案为 \(lim\)

\[lim = \frac{\sum_{0 \le i \le j < |T|} val(i, j)}{|T|} \]

把长度移项

\[|T| * lim = \sum_{0 \le i \le j < |T|} val(i, j) \]

这样, 等式右侧的是 \(dp\) 值, 左侧的 \(|T|\) 开一个数组来记录

每次转移的时候 \(dp -= lim\)

因为每转移一次, 位数增加一, 所以只需看最后的 \(dp\) 是否为非负数即可判断该答案是否合法

该 Trick 的关键在于实现 在不记录长度的情况下, 比较两个子序列的价值大小

代码

#include <bits/stdc++.h>
#define int long long
#define double long double
#pragma GCC optimize(2)
using namespace std;
const int N = 1e5 + 10, MOD = 998244353, INF = 1e9;
const double eps = 1e-17;

int Q_pow(int a, int b){
    int ans = 1, p = a;
    while(b){
        if(b & 1) ans = (ans * p) % MOD;
        b >>= 1;
        p = (p * p) % MOD;
    }
    return ans;
}

int t;
int n, m, val;
string s, ss;

struct Node{
	int son[26];
	int fail, ed;
}tr[N];
int cnt, sum[N], v[N][26];
queue <int> q;

void Insert(string s, int v){
	int p = 0;
	for(auto i : s){
		int ch = i - 'a';
		if(!tr[p].son[ch]) tr[p].son[ch] = ++cnt;
		p = tr[p].son[ch];
	}
	tr[p].ed += v;
}

void Get_fail(){
	for(int i = 0; i < 26; i++){
		if(tr[0].son[i]){
			q.push(tr[0].son[i]);
		}
	}

	while(!q.empty()){
		int tp = q.front();
		q.pop();

		sum[tp] = sum[tr[tp].fail] + tr[tp].ed;
		for(int i = 0; i < 26; i++){
			if(tr[tp].son[i]){
				tr[tr[tp].son[i]].fail = tr[tr[tp].fail].son[i];
				q.push(tr[tp].son[i]);
			}else{
				tr[tp].son[i] = tr[tr[tp].fail].son[i];
			}
		}
	}
}

double f[1001][4001];
int dp[1001][4001], len[1001][4001];

bool Check(double lim){

	for(int i = 0; i <= n; i++){
		for(int j = 0; j <= cnt; j++){
			f[i][j] = -INF;
			dp[i][j] = 0;
			len[i][j] = 0;
		}
	}
	
	f[0][0] = 0;
	dp[0][0] = 0;
	len[0][0] = 0;

	for(int i = 0; i < n; i++){
		for(int j = 0; j <= cnt; j++){
			f[i + 1][j] = f[i][j];
			dp[i + 1][j] = dp[i][j];
			len[i + 1][j] = len[i][j];
		}
		for(int j = 0; j <= cnt; j++){
			int ch = s[i] - 'a';
			int to = tr[j].son[ch];

			if(!to) continue;
			if(f[i + 1][to] + eps < f[i][j] + sum[to] - lim){
				f[i + 1][to] = f[i][j] + sum[to] - lim;
				dp[i + 1][to] = dp[i][j] + sum[to];
				len[i + 1][to] = len[i][j] + 1;
			}
		}
	}

	for(int i = 1; i <= cnt; i++){
		if(f[n][i] >= 0){
			return 1;
		}
	}
	return 0;
}

signed main(){
	// freopen("1.in", "r", stdin);

	cin >> t;

	while(t--){
		cin >> n >> m >> s;
		
		for(int i = 1; i <= m; i++){
			cin >> ss >> val;
			Insert(ss, val);
		}
		Get_fail();

		double l = 0, r = 2e8, ans = 0;

		for(int i = 1; i <= 50; i++){
			double mid = (l + r) / 2;
			if(Check(mid)){
				ans = mid;
				l = mid + 1;
			}else{
				r = mid - 1;
			}
		}

		Check(ans);
		
		for(int i = 1; i <= cnt; i++){
			if(f[n][i] >= 0){
				int ret = dp[n][i] * Q_pow(len[n][i], MOD - 2) % MOD;
				cout << ret << "\n";
				break;
			}
		}

		for(int i = 0; i <= cnt; i++){
			memset(tr[i].son, 0, sizeof(tr[i].son));
			tr[i].ed = tr[i].fail = 0;
			sum[i] = 0;
		}
		cnt = 0;
	}
}

类似的题 - ABC 356 E

题意

求 \(\sum_{(a_i, a_j)} \frac{a_i}{a_j}\) \((i\ != j\ 且 a_i \ge a_j)\)

思路

转化一下, 其实就是在求 \([k * a_j, (k + 1) * a_j)\) 的数量再乘上 \(k\)

类似埃氏筛地做即可

和上面那题类似, 都是把除数均摊到了每一步上

代码

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N = 1e7 + 10;

int n;
int a[N], sum[N];
map <int, int> cnt;

signed main(){
	// freopen("1.in", "r", stdin);

	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		cnt[a[i]]++;
		sum[a[i]]++;
	}

	for(int i = 1; i <= (int)1e7; i++){
		sum[i] += sum[i - 1];
	}

	int ans = 0;
	for(auto i : cnt){
		int ti = 1;
		ans += (sum [i.first + i.first - 1] - sum[i.first]) * i.second;
		for(int j = i.first + i.first; j <= 2e6; j += i.first){
			++ti;
			ans += (sum[j + i.first - 1] - sum[j - 1]) * ti * i.second;
		}
		ans += (i.second - 1) * i.second / 2;
	}
	cout << ans << "\n";
}

标签:ABC,String,9111,sum,tr,tp,son,int,ans
From: https://www.cnblogs.com/Bubblee/p/18400397

相关文章

  • JavaScript 中 structuredClone 和 JSON.parse(JSON.stringify()) 克隆对象的区别
    JavaScript中structuredClone和JSON.parse(JSON.stringify())克隆对象的异同点一、什么是structuredClone?1.structuredClone的发展structuredClone是在ECMAScript2021(ES12)标准中引入的,ECMAScript2021规范正式发布于2021年6月自2022年3月起,该功能适用于最......
  • [ABC221G] Jumping sequence
    MyBlogs[ABC221G]Jumpingsequence做法有点深刻,优化方式非常深刻。首先是哈密顿距离和切比雪夫距离的转化:把坐标系旋转四十五度,变成每次向左上,右上,左下,右下走\(\sqrt2a_i\)的距离,要求最后走到\((A+B,A-B)\)。然后这样可以对于横纵坐标分开做了(非常神奇)。问题转化成了询......
  • 24数学建模ABCE题 保姆级建模思路+可执行代码
    获取资料看文章末尾呢!!!!24数学建模国赛A题详细建模过程+可视化图表+参考论文文本中的第一个问题是关于舞龙队沿等距螺线盘入时的位置和速度计算。具体要求是:1.舞龙队沿螺距为55cm的等距螺线顺时针盘入,龙头前把手的行进速度始终保持1m/s。2.初始时,龙头位于螺线第16......
  • 实现一个功能完备的 C++ String 类保姆指南
    本篇博客,我们将详细讲解如何从头实现一个功能齐全且强大的C++String类,并深入到各个细节。这篇博客将包括每一步的代码实现、解释以及扩展功能的探讨,目标是让初学者也能轻松理解。一、简介1.1、背景介绍在C++编程中,std::string类是最常用的字符串处理工具,它提供了丰......
  • [全网首发]2024国赛数学建模ABCE题完整思路+py(matlab)代码+成品论文参考+持续更新
    AB题详细思路(含问题一问题二模型)CE题问题一代码+思路已经写好[python+matlab两种都会更新需要完整版的看这里:点击链接加入群聊【2024数学建模国赛资料汇总】:http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=lZncBILk30DuPRI1Bd8X-3Djv7ZVZyAv&authKey=kKqNSSEbbZN%2FVKn%2BICO......
  • 2024高教社杯数学建模国赛ABCDE题选题建议+初步分析
    提示:DSC君认为的难度:C<B<A,开放度:A<C<B。D、E题推荐选E题,后续会直接更新E论文和思路,不在这里进行选题分析,以下为A、B、C题选题建议及初步分析A题:“板凳龙”闹元宵A题是数模类赛事很常见的物理类赛题,需要学习不少相关知识。此题涉及对一个动态系统的建模,模拟一支舞龙队伍在......
  • ABC369
     C对于一个等差数列,它里面包含的等差数列(取这个数列的第i位~第j位),必定也是等差数列。寻找等差数列的时候,如果一个等差数列,向最左/最右加1个数后,仍是等差数列,则把它们加上。从而寻找所有场上的等差数列,必定是不重叠的,等差数列彼此独立。从而可以从1遍历到n,O(n)复杂度。对于每......
  • java知识点——String类常用方法
    字符串常用方法: 方法描述int字符串.length()获取字符串长度boolean字符串.equals比较字符串内容是否相等boolean字符串1.equalsIgnoreCase(字符串2)不分大小写比较内容String字符串.toLowerCase()将字符串全部转成小写的String字符串.toUpperCas......
  • String,StringBuffer,StringBuilder有什么区别?
    1.可变性:String类使用了final关键字字符数组保存字符串,所以String对象是不可变的,也就是我们说的常量。而StringBuffer和StringBuilder均继承了AbstractStringBuilder类,且它们的构造方法都是调用父类的构造方法。AbstractStringBuilder类中也使用了字符数组保存字符串,但是没有使用......
  • 发送到PO/PI后查看报文发现会在末尾多给一个空格,后来发现基本上是数字、金额等字段,这
    PO报文发出去数字,金额等字段统统后面都会带有空格但是在abapdebugger看值看不出字段后面有空格<CGHTMXLIST><CGWXMC/><ZJTBM/><XQSL>1.000</XQSL><MEINS>XIA</MEINS><XQGJ>20000000.00</XQGJ><JHAMOUNT>20000000.00</JHAMOUNT><......