首页 > 其他分享 >UVA12096 题解

UVA12096 题解

时间:2023-05-13 23:55:32浏览次数:48  
标签:ch int 题解 v1 vector UVA12096 哈希 define

这道题虽然被评黄,但个人感觉不止普及组难度,而且是一道很有价值的题目。题解区里全是 \(O(n^2logn)\) 的 STL 大法,我来发一篇 \(O(n^2)\) 哈希做法。目前 0ms 喜提最优解。

这道题是 codeforces gym 的题目,本质上就是模拟栈中集合插入,复制,相加,取交集,取并集的过程。注意,这些集合都是不可重集。 如果你直接把这些括号和逗号存下来肯定是不行的,因为它是一个一层套一层的结构,不好直接维护。况且经过复制,括号总数会指数级增长。

既然直接存储不行,我就想用哈希来表示每个集合里的元素。这样每个集合就是一个 vector,里面存储了每个元素对应的哈希值。比如集合 { { { } } , { { } , { } } } 的 vector 里存储 { { } } 和 { { } , { } } 这两个字符串的哈希。

下面考虑分别维护这五种操作。

  1. 对于 PUSH 操作,直接新建一个 vector,里面什么也不存推入栈中。

  2. 对于 DUP 操作,直接复制一遍栈顶的 vector 推入栈中。

  3. 对于 UNION 操作,我们需要把两个 vector 里的数全部放入新的 vector 内,并且去重。如果这两个 vector 无序,去重需要 \(O(nlogn)\) 的复杂度,是不能接受的。所以我们在维护 vector 里的哈希值时,不妨使其有序。 这样,在做 UNION 操作时,就可以归并排序去重了,复杂度 \(O(n)\) 。

  4. 对于 INTERSECT 操作,和 UNION 一样归并排序找到重复元素即可。

  5. 对于 Add 操作,这是最复杂的一个操作。记第一个 vector 为 \(v1\),第二个 vector 为 \(v2\)。则我们要把 \(v1\) 这个整体作为一个元素,求出其哈希值并放入 \(v2\)。但注意:不能设计普通的按位乘法哈希。 至于为什么留给大家自己思考。(提示:括号总数巨大)

    虽然普通哈希不行,但我们有一个很好的性质,就是 v1 内的哈希值是有序的,唯一确定的。于是我们可以把哈希设计成这样:设 v1 内的哈希值为 \(h1\),\(h2\)和\(h3\),则有:

    \(H(v1)=((h1+\Delta)\times h2+\Delta)\times h3\%P\)

    其中 \(\Delta\) 和 \(P\) 是自己设定的常数。注意计算哈希时还要在每个元素之间加一个逗号,左右两边加上大括号。然后把 \(H(v1)\) 用插入排序的方法移到 \(v2\) 相应位置。我这里使用了 vector 的 insert 函数,比插入排序快几十倍左右。

最后输出栈顶 vector 的大小即可。

代码:

#include<iostream>
#include<vector>
#include<algorithm>
#define f first 
#define s second
#define gc getchar
#define pc putchar
#define pb push_back
#define sz(a) a.size()
#define st(a) a.begin()
#define ed(a) a.end()
#define all(a) st(a),ed(a)
#define las(a) *a.rbegin()
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
template<class Typ> Typ &Rd(Typ &x){
    x=0; char ch=gc(),sgn=0;
    for(;ch<'0'||ch>'9';ch=gc()) sgn|=ch=='-';
    for(;ch>='0'&&ch<='9';ch=gc()) x=x*10+(ch^48);
    return sgn&&(x=-x),x;
}
template<class Typ> void Wt(Typ x){
    if(x<0) pc('-'),x=-x;
    if(x>9) Wt(x/10);
    pc(x%10^48);
}
const int N=2005;
const int Ad=1000099;
const int Md=998244353;
vector<int> Hs[N]; char op[10];
int cas,n,stk[N],top;
int main(){
    for(Rd(cas);cas;cas--){
        Rd(n),top=0;
        for(int i=1;i<=n;i++){
            Hs[i].clear(),scanf("%s",op+1);
            if(op[1]=='P') stk[++top]=i;
            else if(op[1]=='D'){
                for(auto j:Hs[stk[top]]) Hs[i].pb(j);
                stk[++top]=i;
            }else if(op[1]=='U'){
                int id1=stk[top--],id2=stk[top--],ps=0;
                for(auto cur:Hs[id1]){
                    for(;ps<sz(Hs[id2])&&Hs[id2][ps]<cur;ps++)
                        Hs[i].pb(Hs[id2][ps]);
                    Hs[i].pb(cur);
                    if(ps<sz(Hs[id2])&&Hs[id2][ps]==cur) ps++;
                }
                for(;ps<sz(Hs[id2]);ps++)
                    Hs[i].pb(Hs[id2][ps]);
                stk[++top]=i;
            }else if(op[1]=='I'){
                int id1=stk[top--],id2=stk[top--],ps=0;
                stk[++top]=i;
                for(auto cur:Hs[id1]){
                    while(ps<sz(Hs[id2])&&Hs[id2][ps]<cur) ps++;
                    if(ps<sz(Hs[id2])&&Hs[id2][ps]==cur) Hs[i].pb(cur);
                }
            }else{
                int Hsh=1+Ad,id1=stk[top--],id2=stk[top];
                for(auto cur:Hs[id1]){
                    if(Hsh!=1+Ad) Hsh=((ll)Hsh*2%Md+Ad)%Md;
                    Hsh=((ll)Hsh*cur%Md+Ad)%Md;
                }
                Hsh=((ll)Hsh*3%Md+Ad)%Md;
                if(!count(all(Hs[id2]),Hsh))
                    Hs[id2].insert(lower_bound(all(Hs[id2]),Hsh),Hsh);
            }
            Wt(sz(Hs[stk[top]])),pc('\n');
        }
        puts("***");
    }
    return 0;
}

写在最后:

要真的在联赛出这道题,我说不定就不选这种方法了,虽然保险,但 STL 也有优势,就是简单好写好调试。(而且 CCF 的数据不用多虑)不管怎么说,看到题解区同情 p 党,我也算是 python 的一员,哈希法也算是给 p 党一个可行的尝试方法了,只要把 vector 换成 list 即可。

做黄题让我回想起初一的时光,一去不复返了。也许有一天 OI 会从我脑中淡漠,但那段回忆永远藏着我的心中。

标签:ch,int,题解,v1,vector,UVA12096,哈希,define
From: https://www.cnblogs.com/Illumina/p/17398524.html

相关文章

  • [AGC001E] BBQ Hard题解
    Problem[AGC001E]BBQHard计算:\[\sum_{i=1}^{n}\sum_{j=i+1}^n\binom{a_i+b_i+a_j+b_j}{a_i+a_j}\]其中\(n\leq2\times10^5\),\(a_i,b_i\leq2000\)Solution以\(a_i\)代\(a_i+b_i\)则等价于求\[\sum_{i=1}^{n}\sum_{j=i+1}^n\binom{a_i+a_j}{b_i+b_j}......
  • 【题解】Luogu[P3750] [六省联考 2017] 分手是祝愿
    Link→小清新dp题,纪念自己的700ac(一个点只有两个状态开和不开,且按两下相当于没按,我们可以从大到小有亮的就按一下,如果我们按到了不需要按的键,就需要再按一下使他变回来。设\(f_i\)表示\(i\)个正确选择变为\(i-1\)个的期望操作次数。则有\(\dfrac{i}{n}\)概率按到......
  • 【题解】Luogu[P4900] 食堂
    一到推柿子题。题意即求\(\sum\limits^{n}_{i=1}\sum\limits^{i}_{j=1}\left\{\frac{i}{j}\bmodP\right\}\)对于\(\frac{i}{j}\bmodP\)我们知道即为\(i\)乘上\(j\)的逆元,即为\(i\cdot\mathrm{inv}(j)\)故:\[\begin{aligned}\sum^{n}_{i=1}\sum^{i}_{j=1}\l......
  • 【题解】Luogu[P6003] USACO20JAN Loan Repayment S
    模拟赛第一题(9pts考虑暴力。枚举\(x\),对于每个\(x\),模拟\(k\)天,判断其是否合法,找出最大的\(x\)。时间复杂度:\(O(n^2)\)36pts考虑优化先前暴力算法。我们不难发现当\(x\)合法时,必然有合法\(x_1\),当且仅当\(x_1<x\)。故\(x\)具有单调性,考虑二分答案。对于\(x......
  • 【题解】Luogu[P8818] CSP-S 2022 策略游戏
    一道简单区间rmq分类讨论题,考场上最后5分钟想出来,没写出来,退役了……给定两个序列\(A_{1},\dots,A_{n}\);\(B_1,\dots,B_n\)规定\(C_{i,j}=A_i\timesB_i\)。题目说小L和小Q必定选择最优策略,而小L先选,小Q后选,小L要使得\(C_{i,j}\)尽可能大,小Q要使得\(C_{i,j}\)......
  • 【题解】Luogu[P1879] [USACO06NOV]Corn Fields G
    Link→状压dp典题,看数据范围就能多半猜到是状压。\(M\)行\(N\)列很不舒服,本篇题解规定为\(N\)行\(M\)列。因为说没有哪两块草地相连,我们不妨一行一行考虑,一行中每格只可能是\(0\)或\(1\),所以一行的总不同状态数是\(2^M\)。我们用二进制表示每一行的状态,对于每一行,暴......
  • 【题解】Luogu[P1967] NOIP2013 提高组 货车运输
    Link→很容易想到一个暴力做法,就是跑一遍Floyd,\(F_{i,j}\)表示\(i\)到\(j\)最大载重量,转移\(F_{i,j}=\max\{F_{i,j},\min\{F_{i,k},F_{k,j}\}\}\)。显然时间复杂度\(O(n^3)\)是过不了的。我们发现,因为是求两点路径中使得最小值最大,实际上有一些较小的路径是不会走......
  • CF1698F题解
    考虑一个函数\(f(a)\),它的返回值是一个二维数组\(b\),接受值是一个数组\(a\)。对于所有\(i=1\ton-1\)的\(i\),把\(b_{a_i}{a_{i+1}}++\),然后返回\(b\)。\(f(a)!=f(b)\)且\(a_1=b_1,a_n=b_n\)是无解的充要条件,因为显然对于数组的每次翻转操作它的\(f\)返回值都不会变。\(f(a)!=f(b......
  • CF1777D Score of a Tree 题解
    题目简述给你一个\(n\)个结点根为\(1\)的树。在\(t=0\)时,每个结点都有一个值,为\(0\)或\(1\)。在每一个\(t>0\)时,每个结点的值都会变成其子结点在\(t-1\)时的值的异或和。定义\(S(t)\)为\(t\)时所有结点值的和。定义\(F(A)\)为树在\(0\let\le10^......
  • CF1777C Quiz Master题解
    题目简述给定一个长度为\(n\)的正整数序列\(a\),以及一个正整数\(m\)。在序列\(a\)中选出一个长度为子序列(不是子段)\(b\),\(\foralli\in[1,m],\existsb_j,b_j\)能整除\(i\)。求所有满足条件的序列\(b\)的极差(最大值于最小值的差)的最小值;若无满足条件序列\(b\)......