模拟赛
重拾题解(刚刚写过一版忘保存了)
T1
其实就是个最长公共子序列的变形。把一样的数才匹配换成有倍数关系就匹配。
最长公共子序列:一般转化为最长上升子序列,即在一个串中的数 \(a\),找到它在另一个串中的位置 \(j\),从 \(1 \dots j-1\) 转移即可,取最大值可用树状数组维护前缀最大值。
本题需要找到在第一个串中每个数的倍数在第二个串中的位置,由于调和级数,只会有 \(log\) 个,然后转移时从后往前遍历,防止本层干扰。
code
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
int n,a[N],b[N],pos[N],f[N],c[N],ans;
vector<int> v[N];
inline bool cmp(int &x,int &y) {return x>y;}
inline void add(int x,int v)
{
for(;x<=n;x+=(x&-x)) c[x]=max(c[x],v);
}
inline int que(int x)
{
int res=0;
for(;x;x-=(x&-x)) res=max(c[x],res);
return res;
}
int main()
{
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]),pos[b[i]]=i;
for(int i=1;i<=n;i++)
{
for(int j=1;j*a[i]<=n;j++)
v[i].push_back(pos[j*a[i]]);
sort(v[i].begin(),v[i].end(),cmp);
for(int j:v[i])
{
f[j]=max(f[j],que(j-1)+1);
ans=max(ans,f[j]);
add(j,f[j]);
}
}
printf("%d\n",ans);
return 0;
}
T2 大爷的字符串题
转化题意,就是查询区间众数。
然后开始莫队 \(\mathbb{MQ}\)!
一种直接用回滚,另一种可以维护每一个数在区间中的出现次数,然后再维护出现次数的出现次数。
注意离散化。
其实挺板的。
code
#include<cstdio>
#include<math.h>
#include<algorithm>
#include<unordered_map>
using namespace std;
#define mx(x,y) (x>y?x:y)
#define LL long long
const int N = 2e5+5;
int n,m,a[N],bl[N],tot;
struct Q {int l,r,id;} q[N];
int cnt[N],Cnt[N],ans[N],now;
unordered_map<int,int> mp;
inline bool cmp(Q &x,Q &y)
{
return bl[x.l]==bl[y.l]?((bl[x.l]&1)?(x.r<y.r):(x.r>y.r)):bl[x.l]<bl[y.l];
}
inline void add(int x)
{
Cnt[cnt[x]]--;
cnt[x]++;
Cnt[cnt[x]]++;
now=mx(now,cnt[x]);
}
inline void del(int x)
{
Cnt[cnt[x]]--;
if(now==cnt[x]&&Cnt[cnt[x]]==0) now--;
cnt[x]--;
Cnt[cnt[x]]++;
}
void mq()
{
for(int i=1,l=1,r=0;i<=m;i++)
{
int tl=q[i].l,tr=q[i].r;
while(l<tl) del(a[l++]);
while(l>tl) add(a[--l]);
while(r>tr) del(a[r--]);
while(r<tr) add(a[++r]);
ans[q[i].id]=-now;
}
}
int main()
{
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
scanf("%d%d",&n,&m); int len=n/sqrt(m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]),bl[i]=(i-1)/len+1;
if(!mp[a[i]]) mp[a[i]]=++tot;
a[i]=mp[a[i]];
}
for(int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
sort(q+1,q+1+m,cmp);
mq();
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}
T3 Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths
启发式合并。
注意理解题意,是每棵子树内最长的合法链。
因为只有 \(22\) 位,所以考虑状压,一个串是否是回文串,我们只在乎其中出现次数为奇数的字符个数,所以用 \(1\) 表示出现奇数次,\(0\) 表示出现偶数次。
至于串的长度可以直接用深度差分得到。
依旧运用差分的思想,我们预处理根到每个点路径异或和,然后就可以从中截取一段路径。(以下状态都表示到根的异或和)
其实我们最终就是要找到某两个点之间的路径异或和为 \(0\) 或者只有一个 \(1\),那么对于某个状态 \(d[u]\),我们想找到的就是某个点的状态为 \(d[u]\) 或 \(d[u]^(1<<i)\),这样两点间的路径就是合法的。
现在我们有了判断合法的方法,考虑如何对每一棵子树进行求解。如果直接遍历一定是 \(n^2\) 的,所以我们考虑用轻重链划分的方法进行启发式合并。
我们选择整体维护重链的信息(全维护开不下),然后暴力遍历轻链,由于每次合并都会使需要遍历的轻链大小至少翻倍,所以复杂度 \(O(n\log n)\)。
然后每次遍历一棵轻链,在已经合并的子树里找有没有能匹配合法的。然后注意我们维护的只是重儿子所在子树,所以对于那些轻儿子的信息要在处理完清空。
code
#include<bits/stdc++.h>
using namespace std;
#define mx(x,y) (x>y?x:y)
const int N = 1e6+5;
int n,ans[N];
int head[N],tot;
struct E {int u,v,w;} e[N<<1];
inline void add(int u,int v,int w) {e[++tot]={head[u],v,w}; head[u]=tot;}
int sz[N],son[N],dep[N],dfn[N],cnt,L[N],R[N],book[N*10],d[N];
inline void dfs(int u,int f)
{
sz[u]=1; dep[u]=dep[f]+1; L[u]=++cnt; dfn[cnt]=u;
for(int i=head[u];i;i=e[i].u)
{
int v=e[i].v; d[v]=d[u]^e[i].w;
dfs(v,u); sz[u]+=sz[v];
if(sz[son[u]]<sz[v]) son[u]=v;
}
R[u]=cnt;
}
inline void dfs2(int u,bool keep)
{
for(int i=head[u];i;i=e[i].u)
{
int v=e[i].v; if(v==son[u]) continue;
dfs2(v,0); ans[u]=mx(ans[u],ans[v]);
}
if(son[u]) dfs2(son[u],1),ans[u]=mx(ans[u],ans[son[u]]);
if(book[d[u]]) ans[u]=mx(ans[u],book[d[u]]-dep[u]);
for(int i=0;i<22;i++) if(book[d[u]^(1<<i)]) ans[u]=mx(ans[u],book[d[u]^(1<<i)]-dep[u]);
book[d[u]]=mx(book[d[u]],dep[u]);
for(int i=head[u];i;i=e[i].u)
{
int v=e[i].v; if(v==son[u]) continue;
for(int j=L[v];j<=R[v];j++)
{
int x=dfn[j];
if(book[d[x]]) ans[u]=mx(ans[u],book[d[x]]+dep[x]-(dep[u]<<1));
for(int k=0;k<22;k++) if(book[d[x]^(1<<k)]) ans[u]=mx(ans[u],book[d[x]^(1<<k)]+dep[x]-(dep[u]<<1));
}
for(int j=L[v];j<=R[v];j++)
{
int x=dfn[j];
book[d[x]]=mx(book[d[x]],dep[x]);
}
}
if(!keep) for(int i=L[u];i<=R[u];i++) book[d[dfn[i]]]=0;
}
int main()
{
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
int x; char c; scanf("%d %c",&x,&c);
add(x,i,1<<(c-'a'));
}
dfs(1,1); dfs2(1,1);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}