【题解】2022ICPC杭州-K

翻译

原题链接
在这里插入图片描述
  简述一下就是每次询问重新定义一个字母排序表,问在这个顺序下n个字符串的序列的逆序数是多少。

字典树计算逆序数

  先考虑初始状况下,即 a < b < . . . < z a<b<...<z a<b<...<z的情况下,逆序数如何计算。而对于这种多字符串问题,优先考虑的肯定是字典树,我们可以对所有字符串构建一棵字典树,在字典树的每个节点上记录所有经过它的字符串的下标,然后计算两种情况的逆序数:

  (1) S i S_{i} Si S j S_{j} Sj的前缀,则它们在字典树上的情况一定会在某一个节点 n o d e t node_{t} nodet处满足 i ∈ n o d e t , j ∈ n o d e t i \in node_{t}, j \in node_{t} inodet,jnodet,而 i i i将不会出现在 n o d e t node_{t} nodet的任何子节点上,假如 i > j i > j i>j,那就产生了一组逆序对。我们记这种情况的总逆序数为 A N S 1 ANS_{1} ANS1

  (2) S i S_{i} Si S j S_{j} Sj存在一位不一样的字符,则一定存在一个节点 n o d e t node_{t} nodet,在这里两个字符串走了不同的路,称这两个字符串在这个节点上出现了分歧(后面要用)。于是我们可以遍历每个节点下的两条分支,在这两个分支中跑一遍求逆序数,采用双指针的方式,类似归并排序时求逆序数,复杂度大约为 O ( 13 ∗ 26 ∗ n ) O(13*26*n) O(1326n),记这种情况的总逆序对数为 A N S 2 ANS_{2} ANS2

  于是,答案就是 A N S 1 + A N S 2 ANS_{1}+ANS_{2} ANS1+ANS2

打乱字母表下如何计算逆序数

  那么好,现在考虑字母表排序打乱的情况,上述过程会有什么变化。容易发现,第一种情况不会受到任何影响,仍然是 A N S 1 ANS_{1} ANS1

  对于第二种情况,假如新的顺序变成了 a > b a>b a>b,发现原本因为分歧原因为 a a a b b b的字符串大小关系发生变化,在初始的排序下,我们不再需要它们的逆序对,而是要顺序对。而这种变化会在 a a a b b b大小关系变化时同时发生,可以将所有因此需要调整的地方统一处理。

  思路可能有点抽象,直接上方法。不再直接求 A N S 2 ANS_{2} ANS2,而是两个列表 w 1 [ 26 ] [ 26 ] w_{1}[26][26] w1[26][26] w 2 [ 26 ] [ 26 ] w_{2}[26][26] w2[26][26],其中 w 1 [ c 1 ] [ c 2 ] w1[c_{1}][c_{2}] w1[c1][c2]表示两个字符串因为 c 1 c_{1} c1 c 2 c_{2} c2产生了分歧而产生的顺序对数, w 2 w_{2} w2则是逆序对数,则初始的 A N S 2 ANS2 ANS2其实可以直接表示为:

A N S 2 = ∑ i = 0 25 ∑ j = i + 1 25 w 2 [ i ] [ j ] ANS_{2}=\sum_{i=0}^{25}\sum_{j=i+1}^{25}w_{2}[i][j] ANS2=i=025j=i+125w2[i][j]

  而在新的顺序下,我们设一个数组 w e i g h t [ 26 ] weight[26] weight[26]表示 a a a z z z的权重,则 A N S 2 ANS_{2} ANS2可以表示为:

A N S 2 = ∑ i = 0 25 ∑ j = i + 1 25 w 1 [ i ] [ j ] ∗ ( i f w e i g h t [ i ] > w e i g h t [ j ] ) ANS_{2}=\sum_{i=0}^{25}\sum_{j=i+1}^{25}w_{1}[i][j]*(if\ weight[i]>weight[j]) ANS2=i=025j=i+125w1[i][j](if weight[i]>weight[j])
+ ∑ i = 0 25 ∑ j = i + 1 25 w 2 [ i ] [ j ] ∗ ( i f w e i g h t [ i ] < w e i g h t [ j ] ) \ \ \ \ \ \ \ \ \ \ \ \ +\sum_{i=0}^{25}\sum_{j=i+1}^{25}w_{2}[i][j]*(if\ weight[i]<weight[j])             +i=025j=i+125w2[i][j](if weight[i]<weight[j])

  最后别忘了加上 A N S 1 ANS_{1} ANS1

时间复杂度

  建树阶段为 O ( ∑ i = 1 n ∣ S i ∣ ) O(\sum_{i=1}^{n}|S_{i}|) O(i=1nSi)
  求 A N S 1 ANS_{1} ANS1阶段为 O ( ∑ i = 1 n ∣ S i ∣ ) O(\sum_{i=1}^{n}|S_{i}|) O(i=1nSi)
  求 w 1 w_{1} w1 w 2 w_{2} w2阶段为 O ( 13 ∗ 26 ∗ ∑ i = 1 n ∣ S i ∣ ) O(13*26*\sum_{i=1}^{n}|S_{i}|) O(1326i=1nSi)
  回答询问阶段为 O ( 13 ∗ 26 ∗ q ) O(13*26*q) O(1326q)

  注意,上述时间复杂度只是大约,大概率达不到,且中间有一些微微的剪枝优化

  此外,千万不要新建一个vector并用另一个vector给它赋值,只为了弄个新的变量名方便写代码,因为vector赋值是默认 O ( s i z e ) O(size) O(size)一个一个拷贝过去的,而不是指针!!!(因为这个TLE了半天)

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
struct node{int nxt[26];int value=-1;int h;vector<int> v;
} G[1000006];
int H[500005];
int new_weight[26];
int szG=1;
int w;     // 子串形成的逆序对数
int w1[26][26], w2[26][26];   // i与j的比较中产生的逆序对数, 反转后产生的逆序对数
int n, q;
inline void ct(int x, int y) {    // 求w1顺序对, w2逆序对if(G[x].value > G[y].value) swap(x, y);    // 保证分别为a子树,b子树int value1 = G[x].value, value2 = G[y].value;int t1 = 0, t2 = 0;while(t1 < G[x].v.size() && t2 < G[y].v.size()) {if(G[x].v[t1] < G[y].v[t2]) {w2[value1][value2] += G[y].v.size() - t2;t1 ++;} else {w1[value1][value2] += G[x].v.size() - t1;t2 ++;}}
}
inline void ct2(int x) {    // 求ANS1,即前缀关系的逆序数vector<int> v1, v2;     // 消失的,剩余的for(int i=0;i<G[x].v.size();i++) {int now = G[x].v[i];if(H[now] == G[x].h) {v1.push_back(now);} else {v2.push_back(now);}}int t1 = 0, t2 = 0;while(t1 < v1.size() && t2 < v2.size()) {if(v1[t1] < v2[t2]) {t1 ++;} else {w += v1.size() - t1;t2 ++;}}
}
inline void dfs(int id) { if(id != 0 && G[id].v.size() <= 1) return;for(int i=0;i<26;i++) {if(G[id].nxt[i] == 0) continue;for(int j=i+1;j<26;j++) {if(G[id].nxt[j] == 0) continue;ct(G[id].nxt[i], G[id].nxt[j]);}}ct2(id);for(int i=0;i<26;i++) {if(G[id].nxt[i] == 0) continue;dfs(G[id].nxt[i]);}
}
signed main() {cin>>n>>q;for(int i=1;i<=n;i++) {string s; cin>>s;H[i] = s.length();int id = 0;   // 根for(int j=0;j<s.size();j++) {if(G[id].nxt[s[j]-'a'] == 0) {   // 下一个节点不存在G[szG].value = s[j] - 'a';G[szG].h = G[id].h + 1;G[id].nxt[s[j]-'a'] = szG;szG++;}id = G[id].nxt[s[j]-'a'];G[id].v.push_back(i);}}dfs(0);while(q--) {           string s; cin>>s;for(int i=0;i<26;i++) {new_weight[s[i]-'a'] = i;}int sum = 0;for(int i=0;i<26;i++) {for(int j=i+1;j<26;j++) {if(new_weight[i] < new_weight[j])sum += w1[i][j];elsesum += w2[i][j];}}printf("%lld\n", w + sum);}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1549887.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Arch - 架构安全性_验证(Verification)

文章目录 OverView导图1. 引言&#xff1a;数据验证的重要性概述2. 数据验证的基本概念3. 数据验证的层次前端验证后端验证 4. 数据验证的标准做法5. 自定义校验注解6. 校验结果的处理7. 性能考虑与副作用8. 小结 OverView 即使只限定在“软件架构设计”这个语境下&#xff0c…

金融科技革命:API接口开放平台,畅通金融服务之路

金融科技是近年来蓬勃发展的领域&#xff0c;它利用先进的技术手段来改善和创新金融服务。在金融科技的革命中&#xff0c;API接口开放平台扮演着重要的角色&#xff0c;它通过提供统一的接口服务&#xff0c;让金融机构和其他行业能够更方便地进行数据交换和合作。本文将以挖数…

聚星文社最新风格图库角色

聚星文社最新风格图库角色涵盖了各种不同的风格和类型。以下是一些可能的角色风格&#xff1a; Docs聚星文社https://iimenvrieak.feishu.cn/docx/ZhRNdEWT6oGdCwxdhOPcdds7nof 现代都市风格角色&#xff1a;这种角色通常穿着时尚的衣服&#xff0c;有时尚的发型和化妆。他们可…

STM32+PWM+DMA驱动WS2812 —— 2024年9月24日

一、项目简介 采用STM32f103C8t6单片机&#xff0c;使用HAL库编写。项目中针对初学者驱动WS2812时会遇到的一些问题&#xff0c;给出了解决方案。 二、ws2812驱动原理 WS2812采用单线归零码的通讯方式&#xff0c;即利用高低电平的持续时间来确定0和1。这种通信方式优点是只需…

IO端口与IO接口

I/O端口和I/O接口是计算机系统中用于连接外部设备的关键组成部分&#xff0c;两者密切相关&#xff0c;但又有明显的区别&#xff1a; I/O端口 (I/O Port): 定义: I/O端口是内存地址空间中的一组特殊地址&#xff0c;用于与外部设备进行数据交换。CPU通过向这些特定的地址写入…

muduo网络库介绍

文章目录 MuduoServer常见接口TcpServer类EventLoop类TcpConnection类 服务器搭建Client常见接口TcpClient类 客户端搭建 Muduo Muduo是陈硕大佬开发的,一个基于非阻塞IO和事件驱动的C高并发网络编程库 这是一个基于主从Reactor模型的网络编程库,线程模型是one loop per thre…

加法器以及标志位

加法器的结构&#xff1a; OF&#xff08;溢出标志位&#xff09;&#xff0c;SF&#xff08;符号标志位&#xff09;&#xff0c;ZF&#xff08;0标志位&#xff09;&#xff0c;ZF&#xff08;进位/借位标志位&#xff09; 有符号数看标志位&#xff1a;OF&#xff0c;SF 无符…

Stable Diffusion绘画 | 插件-Deforum:动态视频生成

Deforum 与 AnimateDiff 不太一样&#xff0c; AnimateDiff 是生成丝滑变化视频的&#xff0c;而 Deforum 的丝滑程度远远没有 AnimateDiff 好。 它是根据对比前面一帧的画面&#xff0c;然后不断生成新的相似图片&#xff0c;来组合成一个完整的视频。 Deforum 的优点在于可…

AI Agent应用出路到底在哪?

1 Agent/Function Call 的定义 Overview of a LLM-powered autonomous agent system&#xff1a; Agent学会调用外部应用程序接口&#xff0c;以获取模型权重中缺失的额外信息&#xff08;预训练后通常难以更改&#xff09;&#xff0c;包括当前信息、代码执行能力、专有信息源…

【Godot4.3】简单物理模拟之圆粒子碰撞检测

概述 最近开始研究游戏物理的内容&#xff0c;研究运动、速度、加速度之类的内容。也开始模仿一些简单的粒子模拟。这些都是一些基础、简单且古老的算法&#xff0c;但是对于理解游戏内的物理模拟很有帮助。甚至你可以在js、Python或者其他程序语言中实现它们。 图形的碰撞检…

详解JavaScript中属性的特性getOwnPropertyDescriptor()等

属性的特性 可以认为一个属性包含一个名字和4个特性&#xff0c;它的值&#xff0c;可写性&#xff0c;可枚举性&#xff0c;可配置性。 因此&#xff0c;存储器属性的4个特性&#xff0c;读取&#xff0c;写入&#xff0c;可枚举&#xff0c;可配置。 定义了一个“属性描述…

Unity实战案例全解析:RTS游戏的框选和阵型功能(2) 生成选择框

前篇&#xff1a;Unity实战案例全解析&#xff1a;RTS游戏的框选和阵型功能&#xff08;1&#xff09; 基础要素-CSDN博客 本案例来源于unity唐老狮&#xff0c;有兴趣的小伙伴可以去泰克在线观看该课程 【唐老狮】Unity实现 即时战略游戏 阵型功能 - 泰课在线 -- 志存高远&…

C++入门基础知识90(实例)——实例15【求两数的最大公约数】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于求两数的最大公约数的相关内容&#xff…

Linux网络:网络编程套接字

socket 套接字 socket常见API 创建套接字&#xff1a;&#xff08;TCP/UDP&#xff0c;客户端服务器&#xff09; int socket(int domain, int type, int protocol);绑定端口号&#xff1a;&#xff08;TCP/UDP&#xff0c;服务器&#xff09; int listen(int sockfd, int …

完全二叉树的节点个数 C++ 简单问题

完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层&#xff0c;则该层包含 1~ 2h 个节点。 示例 1&#xff…

C语言 | Leetcode C语言题解之第445题两数相加II

题目&#xff1a; 题解&#xff1a; struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){int stack1[100];int stack2[100];int top1 0;int top2 0;int carry 0;int sum 0;struct ListNode* temp NULL;struct ListNode* head NULL;while (l1) {…

Go语言中的深拷贝:概念、实现与局限

前不久&#xff0c;在“Gopher部落”知识星球[1]上回答了一个Gopher关于深拷贝(Deep Copy)的问题&#xff0c;让我感觉是时候探讨一下深拷贝技术了。 在日常开发工作中&#xff0c;深拷贝的使用频率相对较低&#xff0c;可能有80%的时间不需要使用深拷贝&#xff0c;只有在特定…

行为设计模式 -模板方法模式- JAVA

模板方法模式 一 .简介二. 案例2.1 抽象类&#xff08;Abstract Class&#xff09;2.2 具体子类&#xff08;Concrete Class&#xff09;2.3 测试 三. 结论3.1 优缺点3.2 适用场景3.3 要点 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接…

springboot网上商城源码分享

开头&#xff1a;springboot网上商城 题目&#xff1a;springboot网上商城 主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Mysql|大数据|SSM|SpringBoot|Vue|Jsp|MYSQL等)、学习资料、JAVA源码、技术咨询 文末联系获取 感兴趣可以先收藏起来&#xff0c;以防走丢&…

指针变量作为函数参数

int main() {char* LPFileBuffer NULL;//接收堆区的指针变量const char* m_fileName "E:\\c\\windowspad.exe";//一个char*的指针变量if (!ReadExeFile(m_fileName, LPFileBuffer)){return -1;}} //接收两个char*变量 OOL ReadExeFile(__in const char* m_fileName…