【字典树 单调栈】P9218 「TAOI-1」Apollo|普及+
本文涉及知识点
C++前缀树(字典树)
C++单调栈
P9218 「TAOI-1」Apollo
题目背景
Execution.
这题原来叫 std::cout << std::fixed << std::setprecision(1) << 6.5 << '\n';
。
[被当事人删掉的图片.jpg]
【Upd 2023/04/15 21:42】添加了一组 Hack 数据位于 Subtask 2,#13。现在所有赛时的 50\bm{50}50 分提交理论上均只能获得 30\bm{30}30 分。
题目描述
给出 nnn 个十进制小数 a1…ana_1 \dots a_na1…an。
对于一个 数 aaa,定义其精度 f(a)f(a)f(a) 表示最小的非负整数 kkk 使得 10k×a10^k\times a10k×a 为整数;对于整数 aaa,定义 f(a)=0f(a) = 0f(a)=0。对于两个十进制小数 a,ba, ba,b,定义 g(a,b)g(a, b)g(a,b) 为对于所有 数 c∈[min(a,b),max(a,b)]c \in [\min(a,b), \max(a,b)]c∈[min(a,b),max(a,b)] 的 f(c)f(c)f(c) 的最小值。
对于所有 1≤i≤n1 \leq i \leq n1≤i≤n,你需要求出 ∑j=1ng(ai,aj)\sum\limits_{j=1}^ng(a_i, a_j)j=1∑ng(ai,aj) 的值并输出。
定义十进制小数是一个含有整数部分和小数部分的数,其小数部分不为 000。之所以描述得这么愚蠢是因为保证输入的每个数都有小数点,但是好像无论什么写法都会有人提出不满,真是抱歉了。所以还是得看看被当事人删掉的图片。所以我在这里写闲话有人看得到吗。
输入格式
第一行一个整数 nnn。
接下来 nnn 行,每行一个十进制小数 aia_iai。
输出格式
nnn 行,每行一个整数,分别表示 i=1…ni = 1 \dots ni=1…n 对应的答案。
输入输出样例 #1
输入 #1
5
11.4514
11.4778
11.1338
11.1236
11.4789
输出 #1
10
11
9
9
11
输入输出样例 #2
输入 #2
8
1.1145
1.114
1.1145
1.514
1.19198
1.1154
1.114
1.1145
输出 #2
24
21
24
10
18
22
21
24
说明/提示
数据范围
本题采用捆绑测试。
令 ∑i=1nf(ai)=t\sum\limits_{i=1}^n f(a_i) = ti=1∑nf(ai)=t。
- Subtask 1(15 points):ai≤10a_i \leq 10ai≤10,n≤5n \leq 5n≤5,t≤10t \leq 10t≤10。
- Subtask 2(15 points):ai≤10a_i \leq 10ai≤10,n≤100n \leq 100n≤100,t≤1000t \leq 1000t≤1000。
- Subtask 3(20 points):n≤1722n \leq 1722n≤1722。
- Subtask 4(15 points):ai≤1a_i \leq 1ai≤1。
- Subtask 5(35 points):无特殊限制。
对于所有数据,0<ai<1090 \lt a_i \lt 10^90<ai<109,1≤n≤1051 \leq n \leq 10^51≤n≤105,1≤t≤3×1061 \leq t \leq 3 \times 10^61≤t≤3×106,保证 ai\bm{a_i}ai 没有后导 0\color{black}\bm 00,不保证 ai\bm{a_i}ai 互不相同。
样例解释
以 i=1i = 1i=1 为例:
- j=1j = 1j=1:取 c=11.4514c = 11.4514c=11.4514,f(c)=4f(c) = 4f(c)=4;
- j=2j = 2j=2:取 c=11.46c = 11.46c=11.46,f(c)=2f(c) = 2f(c)=2;
- j=3j = 3j=3:取 c=11.2c = 11.2c=11.2,f(c)=1f(c) = 1f(c)=1;
- j=4j = 4j=4:取 c=11.3c = 11.3c=11.3,f(c)=1f(c) = 1f(c)=1;
- j=5j = 5j=5:取 c=11.47c = 11.47c=11.47,f(c)=2f(c) = 2f(c)=2。
故 ∑j=1ng(a1,aj)=4+2+1+1+2=10\sum\limits_{j=1}^n g(a_1, a_j) = 4 + 2 + 1 + 1 + 2 = 10j=1∑ng(a1,aj)=4+2+1+1+2=10。对于同一个 jjj,上文给出的所有 ccc,都可能有其它的不同的 ccc 满足 f(c)f(c)f(c) 同样最小。
字典树
不失一般性,a <= b。
如果a和b整数部分不同,c = b的整数部分,即f(c )等于0。
只需要考虑整数部分相同,令小数部分公共前缀为len:
a,0 == len,c = b/10*10,f( c) == 1。如果a或b没小数部分,则f(c)=0f(c)=0f(c)=0。
b,len > 0,且两者的小数部分大于len。c=b/10len+1f(c)=len+1c = b / 10^{len+1} f(c)=len+1c=b/10len+1f(c)=len+1。
c, len > 0,且a的小数部分长度为len。c=a,即f(c)=lenf(c)=lenf(c)=len。包括a和b的小数部分相等。
总结:c和d整数部分想相同,c的小数部分长度<=d的小数部分长度g(c,d)=c和d小数部分的公共前缀len + c小数部分的长度不是len。
建立若干字典树,整数部分相同的在一颗树,不同的一在一颗字典树。
s[i]是a[i]的小数部分,如果a[i]是整数,则s[i]是空串。
处理a[i]的过程:
cnt[j]记录s中包括前缀s[i][0…j-1]的数量。
ans[i]=∑(cnt)+cnt[0]−xans[i]=\sum(cnt)+cnt[0]-xans[i]=∑(cnt)+cnt[0]−x
x1 = cout(s[j]) 包括前缀s[i]的数量。即s[i]子树的节点数量。
x2 cout(s[j]) 是s[i]的前缀,且s[j]≠s[i]s[j] \neq s[i]s[j]=s[i]。即s[i][0…s[i].长度-2]的叶节点数量。
内存限制512M,很充足。
本题的字典树有如下性质:
一,可以为空。
二,需要记录叶子节点数量。
三,需要记录子节点的数量。
四,需要增加字符串。
五,需要顺序查询节点。
代码
核心代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include<array>#include <bitset>
using namespace std;template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {in >> pr.first >> pr.second;return in;
}template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t);return in;
}template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);return in;
}template<class T = int>
vector<T> Read() {int n;cin >> n;vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}
template<class T = int>
vector<T> ReadNotNum() {vector<T> ret;T tmp;while (cin >> tmp) {ret.emplace_back(tmp);if ('\n' == cin.get()) { break; }}return ret;
}template<class T = int>
vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}template<int N = 1'000'000>
class COutBuff
{
public:COutBuff() {m_p = puffer;}template<class T>void write(T x) {int num[28], sp = 0;if (x < 0)*m_p++ = '-', x = -x;if (!x)*m_p++ = 48;while (x)num[++sp] = x % 10, x /= 10;while (sp)*m_p++ = num[sp--] + 48;AuotToFile();}void writestr(const char* sz) {strcpy(m_p, sz);m_p += strlen(sz);AuotToFile();}inline void write(char ch){*m_p++ = ch;AuotToFile();}inline void ToFile() {fwrite(puffer, 1, m_p - puffer, stdout);m_p = puffer;}~COutBuff() {ToFile();}
private:inline void AuotToFile() {if (m_p - puffer > N - 100) {ToFile();}}char puffer[N], * m_p;
};template<int N = 1'000'000>
class CInBuff
{
public:inline CInBuff() {}inline CInBuff<N>& operator>>(char& ch) {FileToBuf();while (('\r' == *S) || ('\n' == *S) || (' ' == *S)) { S++; }//忽略空格和回车ch = *S++;return *this;}inline CInBuff<N>& operator>>(int& val) {FileToBuf();int x(0), f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行 return *this;}inline CInBuff& operator>>(long long& val) {FileToBuf();long long x(0); int f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行return *this;}template<class T1, class T2>inline CInBuff& operator>>(pair<T1, T2>& val) {*this >> val.first >> val.second;return *this;}template<class T1, class T2, class T3>inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val);return *this;}template<class T1, class T2, class T3, class T4>inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);return *this;}template<class T = int>inline CInBuff& operator>>(vector<T>& val) {int n;*this >> n;val.resize(n);for (int i = 0; i < n; i++) {*this >> val[i];}return *this;}template<class T = int>vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {*this >> ret[i];}return ret;}template<class T = int>vector<T> Read() {vector<T> ret;*this >> ret;return ret;}
private:inline void FileToBuf() {const int canRead = m_iWritePos - (S - buffer);if (canRead >= 100) { return; }if (m_bFinish) { return; }for (int i = 0; i < canRead; i++){buffer[i] = S[i];//memcpy出错 }m_iWritePos = canRead;buffer[m_iWritePos] = 0;S = buffer;int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);if (readCnt <= 0) { m_bFinish = true; return; }m_iWritePos += readCnt;buffer[m_iWritePos] = 0;S = buffer;}int m_iWritePos = 0; bool m_bFinish = false;char buffer[N + 10], * S = buffer;
};struct CMyTrieNode
{array<CMyTrieNode*, 10> m_child;int m_iCnt = 0;//后代数量int m_iLeafeCnt = 0;//叶子节点数量
};
class CMyTrie {
public:void Add(const char* pStr, const char* pEnd) {auto p = &m_root;for (; pStr < pEnd; pStr++) {p->m_iCnt++;if (nullptr == p->m_child[*pStr - '0']) {p->m_child[*pStr - '0'] = new CMyTrieNode();}p = p->m_child[*pStr - '0'];}p->m_iCnt++;p->m_iLeafeCnt++;}CMyTrieNode m_root;
};class Solution {
public:vector<long long> Ans(const int N, vector<string>& num) {vector<string> str1s, str2s;for (const auto& s : num) {int pos = s.find('.');if (-1 == pos) {str1s.emplace_back(s);str2s.emplace_back("");}else {str1s.emplace_back(s.substr(0, pos));str2s.emplace_back(s.substr(pos + 1));m_tries[str1s.back()].Add(s.data() + pos + 1, s.data() + s.length());}}vector<long long> ans;for (int i = 0; i < str1s.size(); i++) {auto& trie = m_tries[str1s[i]];auto p = &(trie.m_root);long long cur = p->m_iCnt;for (const auto& ch : str2s[i]) {p = p->m_child[ch - '0'];cur += p->m_iCnt;cur -= p->m_iLeafeCnt;}cur -= (p->m_iCnt - p->m_iLeafeCnt);ans.emplace_back(cur);}return ans;}unordered_map<string, CMyTrie> m_tries;
};int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUG ios::sync_with_stdio(0); cin.tie(nullptr);//CInBuff<> in; COutBuff<10'000'000> ob; int N;cin >> N;vector<string> num(N);for (int r = 0; r < N; r++) {cin >> num[r];}
#ifdef _DEBUG //printf("N=%d", N);Out(num, ",num=");//Out(a, ",a=");//Out(ab, ",ab=");//Out(par, "par=");//Out(que, "que=");//Out(B, "B=");
#endif // DEBUG auto res = Solution().Ans(num.size(), num);for (const auto& i : res) {cout << i << "\n";}return 0;
};
单元测试
vector< string > num;TEST_METHOD(TestMethod01){num = { "1","1.2","1.23","1.24" };auto res = Solution().Ans(num.size(), num);AssertV({ 0,3,5,5 }, res);}TEST_METHOD(TestMethod02){num = { "11.4514","11.4778","11.1338","11.1236","11.4789" };auto res = Solution().Ans(num.size(), num);AssertV({ 10,11,9,9,11 }, res);}TEST_METHOD(TestMethod03){num = { "1.1145","1.114","1.1145","1.514","1.19198","1.1154","1.114","1.1145" };auto res = Solution().Ans(num.size(), num);AssertV({ 24,21,24,10,18,22,21,24 }, res);}
单调栈+差分数组
h(d,e)=d小数部分和e小数部分的最长公共前缀长度。
过于复杂,可用于理解单调栈,用于解本题过于复杂。
性质一:ai和aj的整数部分不同,则g(ai,aj)等于0a_i和a_j的整数部分不同,则g(a_i,a_j)等于0ai和aj的整数部分不同,则g(ai,aj)等于0,故只需要处理整数部分相同的数。
性质二:如果ai等于0或aj等于0→g(ai,aj)等于0a_i等于0或a_j等于0 \rightarrow g(a_i,a_j)等于0ai等于0或aj等于0→g(ai,aj)等于0。故小数部分为空的数忽略。
实现一(桶排序思想):整数相同的数放到一个桶中,小数部分直接转成字符串放到容器中,升序。
性质三:同一个桶相邻元素d和e相等,则g(d,e)=d的小数部分。
性质四:如果d≠ed \neq ed=e,不失一般性,d < e,d的小数位数是n1,e的小数位数是n2,de的公共前缀长度是n3。如果n3==n1,则g(d,e) =n3,否则g(d,e)=n3+1。
总结一:如果d的小数部分是e小数部分的前缀,则g(d,e)=h(d,e);否则g(d,e)=h(d,e)+1。
性质五:令某个桶的小数部分是v,0≤i<j<k<v.size()0\le i < j < k < v.size()0≤i<j<k<v.size()。h(i,j)表示v[i]和v[j]的最长公共前缀长度。
性质五一 h(i,j)≥h(i,k)\ge h(i,k)≥h(i,k)。
推论一:如果v[i]和v[j]的公共前缀不是v[i],则v[i]和v[k]的公共前缀也不是v[i]。
性质五二:如果h(i,j)==h(i,k)==x,则∀jk,h(i,jk)=x,j≤jk≤k\forall jk,h(i,jk)=x,j \le jk \le k∀jk,h(i,jk)=x,j≤jk≤k。
性质五三:h(i,k)≤\le≤h(j,k),故h(i-1,ij)≤\le≤h(i,ij)。→∀ij\rightarrow \forall ij→∀ij h(i-1,ij)都相等,因为,v[j∼k]的前x位都相等[j\sim k]的前x位都相等[j∼k]的前x位都相等。故可以永久删除k的信息。
性质六:g(i−1,i)≥g(i,j)→g(i−1,j)==g(i,j)g(i-1,i) \ge g(i,j) \rightarrow g(i-1,j) ==g(i,j)g(i−1,i)≥g(i,j)→g(i−1,j)==g(i,j) ;否则,g(i−1,j)=g(i,j)g(i-1,j)=g(i,j)g(i−1,j)=g(i,j)
实现
Do2 处理a[i+1∼N−1]i+1 \sim N-1]i+1∼N−1]
迭代前
sta的每个元素包括两个{x,cnt} 表示g(ai,aj)==xg(a_i,a_j)==xg(ai,aj)==x的数量,i<j<Ni < j < Ni<j<N。从栈底到栈顶,x升序。
迭代过程:
x1 = g(i-1,i)
cnt1=1
当栈顶x ≥\ge≥ x1
{cnt1 += 栈顶cnt
出栈
}
{x1,cnt1}入栈
int sum = ∑(x+1)×cnt\sum (x+1) \times cnt∑(x+1)×cnt
∑j:iN−1g(ai,aj)=sum−栈顶cnt×(栈顶x==ai小数位数)\sum_{j:i}^{N-1}g(a_i,a_j)=sum - 栈顶cnt \times (栈顶x ==a_i小数位数)∑j:iN−1g(ai,aj)=sum−栈顶cnt×(栈顶x==ai小数位数)
Do 处理a[0∼i−10 \sim i-10∼i−1]
和处理i∼N−1i \sim N-1i∼N−1类似。
∑j:0N−1g(ai,aj)=∑j:0ig(ai,aj)+∑j:iN−1g(ai,aj)−ai小数的位数\sum_{j:0}^{N-1}g(a_i,a_j)=\sum_{j:0}^{i}g(a_i,a_j)+\sum_{j:i}^{N-1}g(a_i,a_j)-a_i小数的位数∑j:0N−1g(ai,aj)=∑j:0ig(ai,aj)+∑j:iN−1g(ai,aj)−ai小数的位数
注意:
由于d≤ed \le ed≤e,故只有以下三种情况:
一,小数部分互为前缀,即两者相等。
二,d的小数部分是e小数部分的前缀,反之不成立。
三,互不为前缀。
以下不可能成立:
e的小数是d小数部分的前缀,反之不成立。和d≤ed\le ed≤e矛盾。
性质七:d的小数部分是否是e的小数部分的前缀⟺以下条件是否至少一个成立。一,d的小数部分是e小数部分的前缀。二,e的小数部分是d的小数部分的前缀d的小数部分是否是e的小数部分的前缀 \iff 以下条件是否至少一个成立。 一,d的小数部分是e小数部分的前缀。二,e的小数部分是d的小数部分的前缀d的小数部分是否是e的小数部分的前缀⟺以下条件是否至少一个成立。一,d的小数部分是e小数部分的前缀。二,e的小数部分是d的小数部分的前缀
这样就不会重复扣除。在Do2中扣除g(0∼i−1,i)0 \sim i-1,i)0∼i−1,i)。即如下代码:
if (b){diff[i + 1]++;diff[i + 1 + cnt]--;}
大致过程
unorder_map<int,vector<pair<string,int>> mv
通过i枚举各数
mv[第i个数的整数部分].emplace_back(第i个数的小数部分,i)
for([tmp,v]:mv){
处理v
}
代码
核心代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include<array>#include <bitset>
using namespace std;template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {in >> pr.first >> pr.second;return in;
}template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t);return in;
}template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);return in;
}template<class T = int>
vector<T> Read() {int n;cin >> n;vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}
template<class T = int>
vector<T> ReadNotNum() {vector<T> ret;T tmp;while (cin >> tmp) {ret.emplace_back(tmp);if ('\n' == cin.get()) { break; }}return ret;
}template<class T = int>
vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}template<int N = 1'000'000>
class COutBuff
{
public:COutBuff() {m_p = puffer;}template<class T>void write(T x) {int num[28], sp = 0;if (x < 0)*m_p++ = '-', x = -x;if (!x)*m_p++ = 48;while (x)num[++sp] = x % 10, x /= 10;while (sp)*m_p++ = num[sp--] + 48;AuotToFile();}void writestr(const char* sz) {strcpy(m_p, sz);m_p += strlen(sz);AuotToFile();}inline void write(char ch){*m_p++ = ch;AuotToFile();}inline void ToFile() {fwrite(puffer, 1, m_p - puffer, stdout);m_p = puffer;}~COutBuff() {ToFile();}
private:inline void AuotToFile() {if (m_p - puffer > N - 100) {ToFile();}}char puffer[N], * m_p;
};template<int N = 1'000'000>
class CInBuff
{
public:inline CInBuff() {}inline CInBuff<N>& operator>>(char& ch) {FileToBuf();while (('\r' == *S) || ('\n' == *S) || (' ' == *S)) { S++; }//忽略空格和回车ch = *S++;return *this;}inline CInBuff<N>& operator>>(int& val) {FileToBuf();int x(0), f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行 return *this;}inline CInBuff& operator>>(long long& val) {FileToBuf();long long x(0); int f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行return *this;}template<class T1, class T2>inline CInBuff& operator>>(pair<T1, T2>& val) {*this >> val.first >> val.second;return *this;}template<class T1, class T2, class T3>inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val);return *this;}template<class T1, class T2, class T3, class T4>inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);return *this;}template<class T = int>inline CInBuff& operator>>(vector<T>& val) {int n;*this >> n;val.resize(n);for (int i = 0; i < n; i++) {*this >> val[i];}return *this;}template<class T = int>vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {*this >> ret[i];}return ret;}template<class T = int>vector<T> Read() {vector<T> ret;*this >> ret;return ret;}
private:inline void FileToBuf() {const int canRead = m_iWritePos - (S - buffer);if (canRead >= 100) { return; }if (m_bFinish) { return; }for (int i = 0; i < canRead; i++){buffer[i] = S[i];//memcpy出错 }m_iWritePos = canRead;buffer[m_iWritePos] = 0;S = buffer;int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);if (readCnt <= 0) { m_bFinish = true; return; }m_iWritePos += readCnt;buffer[m_iWritePos] = 0;S = buffer;}int m_iWritePos = 0; bool m_bFinish = false;char buffer[N + 10], * S = buffer;
};struct CMyTrieNode
{array<CMyTrieNode*, 10> m_child;int m_iCnt = 0;//后代数量int m_iLeafeCnt = 0;//叶子节点数量
};
class CMyTrie {
public:void Add(const char* pStr, const char* pEnd) {auto p = &m_root;for (; pStr < pEnd; pStr++) {p->m_iCnt++;if (nullptr == p->m_child[*pStr - '0']) {p->m_child[*pStr - '0'] = new CMyTrieNode();}p = p->m_child[*pStr - '0'];}p->m_iCnt++;p->m_iLeafeCnt++;}CMyTrieNode m_root;
};class Solution {public:vector<long long> Ans(const int N, vector<string>& num) {unordered_map<int, vector<pair<string, int>>> mv;int inx = -1;for (const auto& s : num) {++inx;int pos = s.find('.');int i1 = atoi(s.c_str()); if (-1 == pos) {mv[i1].emplace_back("", inx);}else {mv[i1].emplace_back(s.substr(pos + 1), inx);}}vector<long long> ans(N);auto Do = [&](const vector<pair<string, int>>& v) {long long sum = 0;stack<pair<int, int>> sta;for (int i = 1; i < v.size();i++) {const string& s1 = v[i].first;const string& s2 = v[i - 1].first;int j = 0;for (; (j < s1.length()) && (j < s2.length()) && (s1[j] == s2[j]);j++);int cnt = 1;while (sta.size() && (j <= sta.top().first)) {cnt += sta.top().second;sum -= (sta.top().first + 1) * sta.top().second;sta.pop();}sta.emplace(j, cnt);sum += (j + 1) * cnt; ans[v[i].second] += sum ;}};vector<int> diff;auto Do2 = [&](const vector<pair<string, int>>& v ) {long long sum = 0;diff.assign(v.size() + 1, 0);stack<pair<int, int>> sta;for (int i = v.size() - 2; i >= 0;i--) {const string& s1 = v[i].first;const string& s2 = v[i+1].first;int j = 0;for (; (j < s1.length()) && (j < s2.length()) && (s1[j] == s2[j]);j++);int cnt = 1;while (sta.size() && (j <= sta.top().first)) {cnt += sta.top().second;sum -= (sta.top().first + 1) * sta.top().second;sta.pop();}sta.emplace(j, cnt);sum += (j + 1) * cnt;const bool b = (j == v[i].first.length());ans[v[i].second] += sum - (b ? sta.top().second : 0);if (b){diff[i + 1]++;diff[i + 1 + cnt]--;}}int sum1 = 0;for (int i = 0;i < v.size();i++) {sum1 += diff[i];ans[v[i].second] -= sum1;}};inx = -1;for (auto& [tmp, v] : mv) {sort(v.begin(), v.end()); Do(v); Do2(v);for (const auto& [s, inx] : v) {ans[inx] += s.length();} }return ans;}};int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUG ios::sync_with_stdio(0); cin.tie(nullptr);//CInBuff<> in; COutBuff<10'000'000> ob; int N;cin >> N;vector<string> num(N);for (int r = 0; r < N; r++) {cin >> num[r];}
#ifdef _DEBUG //printf("N=%d", N);Out(num, ",num=");//Out(a, ",a=");//Out(ab, ",ab=");//Out(par, "par=");//Out(que, "que=");//Out(B, "B=");
#endif // DEBUG auto res = Solution().Ans(num.size(), num);for (const auto& i : res) {cout << i << "\n";}return 0;
};
扩展阅读
我想对大家说的话 |
---|
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。