#194 sequence(搜索+动态规划+主席树)
Posted gloid
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#194 sequence(搜索+动态规划+主席树)相关的知识,希望对你有一定的参考价值。
考虑按顺序暴搜子序列。如果序列中的数两两不同,显然每次给上一个找到的子序列添上后缀最小值,即为下一个要找的子序列。如果不能再加了就回溯继续考虑后缀次小、第三小……值,直到找到k个子序列。
有重复的数后,考虑后缀k小值只取第一次出现的位置,并在每找到一个子序列后就统计其出现次数。显然这样就能找到所有要找的子序列,因为序列末端选择位置更靠前,后面的选择更多。
求一个序列在另一个序列里的出现次数显然可以dp,即设f[i][j]为第一个序列的i位置和第二个序列的j位置匹配的方案数。当然答案序列可能很长,dp数组可能开不下,不过注意到有ai<=30的部分分,可以猜想这个部分的答案序列长度不会很长,先考虑拿部分分。
每找到一个序列就暴力dp即为O(n2k)。注意到dp时可以直接继承上层dfs的dp数组,于是复杂度O(nk)。这里的k一般来说并不能跑满,实际上是本质不同的答案中出现的子序列个数。但是很容易卡满,比如放99970个30,最后将1到30倒序加入序列,这样每个答案序列都是不同的,就被卡成暴力了。
但上面这个hack数据有比较特殊的地方,即存在于答案中的数字基本上出现次数很少。注意到我们之前的dp实际上可以将做一次的复杂度优化到序列最后一个数的出现次数(*log或+上一个数出现次数)。这样上面的数据就hack不掉了。同时如果要使这部分运算次数增加,本质不同的答案子序列数量又会减少。这样这个做法就根本卡不掉并且跑得飞快了,复杂度O(玄学)。但实际上可以冷静分析一下复杂度,如果某次dp时该数出现次数为x,我们至少就找到了x个答案中的子序列。所以这一部分复杂度其实是O(k)(或*log)的。大概就是所谓卡常卡着卡着发现复杂度对了?
还剩下一点问题,就是上面的dp数组开不下,以及要找后缀k小值。第一个问题直接对dp数组开vector,既然这个做法不会T那当然也不会MLE。第二个是主席树板子题。总复杂度O(klogn)。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<vector> using namespace std; #define ll long long #define N 100010 char getc(){char c=getchar();while ((c<‘A‘||c>‘Z‘)&&(c<‘a‘||c>‘z‘)&&(c<‘0‘||c>‘9‘)) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<‘0‘||c>‘9‘) {if (c==‘-‘) f=-1;c=getchar();} while (c>=‘0‘&&c<=‘9‘) x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,V,a[N],root[N],seed,P,tot,cnt; vector<int> pos[N],f[N]; struct data{int l,r,x,pos; }tree[N<<6]; void ins(int &k,int l,int r,int x,int p) { tree[++cnt]=tree[k];k=cnt; if (l==r) {tree[k].x=1,tree[k].pos=p;return;} int mid=l+r>>1; if (x<=mid) ins(tree[k].l,l,mid,x,p); else ins(tree[k].r,mid+1,r,x,p); tree[k].x=tree[tree[k].l].x+tree[tree[k].r].x; } int query(int k,int l,int r,int x) { if (l==r) return tree[k].pos; int mid=l+r>>1; if (x<=tree[tree[k].l].x) return query(tree[k].l,l,mid,x); else return query(tree[k].r,mid+1,r,x-tree[tree[k].l].x); } int findnxt(int k,int x){return query(root[k+1],0,V,x);} int findpre(int x,int k){return lower_bound(pos[x].begin(),pos[x].end(),k)-pos[x].begin()-1;} void dfs(int k,int cur,int h) { int last=0; while (tot<m&&k<n) { last++;int u=findnxt(k,last); if (u>n) break; int H=(1ll*h*seed+a[u])%P; f[cur+1].clear(); for (int i=0;i<pos[a[u]].size();i++) { if (i) f[cur+1].push_back(f[cur+1][i-1]); else f[cur+1].push_back(0); int x=findpre(a[k],pos[a[u]][i]); if (x>=0) f[cur+1][i]+=f[cur][x]; f[cur+1][i]=min(f[cur+1][i],m-tot); } int v=f[cur+1][pos[a[u]].size()-1]; for (int i=1;i<=v;i++) printf("%d ",H); tot+=v; dfs(u,cur+1,H); } } int main() { freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); n=read(),m=read(),seed=read(),P=read(); pos[a[0]].push_back(0); for (int i=1;i<=n;i++) { pos[a[i]=read()].push_back(i); V=max(V,a[i]); } V++;a[n+1]=V; root[n+2]=0; for (int i=n+1;i>=1;i--) { root[i]=root[i+1]; ins(root[i],0,V,a[i],i); } f[0].push_back(1); dfs(0,0,0); return 0; }
以上是关于#194 sequence(搜索+动态规划+主席树)的主要内容,如果未能解决你的问题,请参考以下文章