树
现在他想解决这样一个问题:给定一颗有根树(根为1),有以下两种操作:
标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个结点,可以打多次标记)。
询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖先)你能帮帮他吗?
树剖
但是暴力能过去
#include <bits/stdc++.h>
constexpr int maxn = 100050;
int fa[maxn];
bool flag[maxn];
int t[maxn], nearfa[maxn], cnt = 1;
int find(int u) {
if (flag[u])
return u;
if (t[u] == cnt)
return nearfa[u];
t[u] = cnt;
return nearfa[u] = find(fa[u]);
}
int main(void) {
flag[1] = 1;
fa[1] = 1;
int n, q;
scanf("%d%d", &n, &q);
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
fa[v] = u;
}
for (int i = 1; i <= n; i++) t[i] = nearfa[i] = 1;
for (int i = 0; i < q; i++) {
char oper;
int u;
scanf("\n%c %d", &oper, &u);
if (oper == 'C') {
if (!flag[u])
flag[u] = 1;
cnt++;
} else
printf("%d\n", find(u));
}
return 0;
}
数据水爆了,可以被卡到\(n^2\),但是跑的比正解还快。
梦幻布丁
\(n\) 个布丁摆成一行,进行 \(m\) 次操作。每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色。
例如,颜色分别为 \(1,2,2,1\) 的四个布丁一共有 \(3\) 段颜色.
我们可以把每种颜色的布丁集合想象成是一个队列,每次操作合并,显然暴力是n的,但是可以启发式合并,把短的合并到长的上去,复杂度就变成了log。
我们先求出原序列的答案,对于每一种颜色都用类似链表的数据结构串起来,并记录下尾节点。每次修改,都根据启发式合并的方法来暴力合并,然后处理一下此次合并对答案的影响(显然答案是不增的)。
#include <iostream>
#include <cstdio>
const int N=1e5+5,M=1e6+5;
int n,m,c[N],sz[M],st[M],f[M],hd[M],nxt[N],ans;
void merge(int x,int y) {
for(int i=hd[x];i;i=nxt[i]) ans-=(c[i-1]==y)+(c[i+1]==y);
for(int i=hd[x];i;i=nxt[i]) c[i]=y;
nxt[st[x]]=hd[y],hd[y]=hd[x],sz[y]+=sz[x];
hd[x]=st[x]=sz[x]=0;
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) {
scanf("%d",&c[i]),f[c[i]]=c[i];
ans+=c[i]!=c[i-1];
if(!hd[c[i]]) st[c[i]]=i;
++sz[c[i]],nxt[i]=hd[c[i]],hd[c[i]]=i;
}
while(m--) {
int opt;
scanf("%d",&opt);
if(opt==2) printf("%d\n",ans);
else {
int x,y;
scanf("%d%d",&x,&y);
if(x==y) continue;
if(sz[f[x]]>sz[f[y]]) std::swap(f[x],f[y]);
if(!sz[f[x]]) continue;
merge(f[x],f[y]);
}
}
return 0;
}
软件包管理器
你决定设计你自己的软件包管理器。不可避免地,你要解决软件包之间的依赖问题。如果软件包 \(a\) 依赖软件包 \(b\),那么安装软件包 \(a\) 以前,必须先安装软件包 \(b\)。同时,如果想要卸载软件包 \(b\),则必须卸载软件包 \(a\)。
现在你已经获得了所有的软件包之间的依赖关系。而且,由于你之前的工作,除 \(0\) 号软件包以外,在你的管理器当中的软件包都会依赖一个且仅一个软件包,而 \(0\) 号软件包不依赖任何一个软件包。且依赖关系不存在环(即不会存在 \(m\) 个软件包 \(a_1,a_2, \dots , a_m\),对于 \(i<m\),\(a_i\) 依赖 \(a_{i+1}\),而 \(a_m\) 依赖 \(a_1\) 的情况)。
现在你要为你的软件包管理器写一个依赖解决程序。根据反馈,用户希望在安装和卸载某个软件包时,快速地知道这个操作实际上会改变多少个软件包的安装状态(即安装操作会安装多少个未安装的软件包,或卸载操作会卸载多少个已安装的软件包),你的任务就是实现这个部分。
注意,安装一个已安装的软件包,或卸载一个未安装的软件包,都不会改变任何软件包的安装状态,即在此情况下,改变安装状态的软件包数为 \(0\)。
每次安装软件,就把根节点到x软件路径上的值全部变为1
同理,每次卸载软件,就把x以及它的子树的值变为0
故我们可以用区间和的思想,每次操作之前记录一下tree[root].sum的值,更新之后再查询一遍tree[root].sum的值,两者之差的绝对值则为答案。
#include<bits/stdc++.h>
using namespace std;
constexpr int maxn =2e5+10;
int n,k,x;
int head[maxn],deep[maxn],father[maxn],size[maxn];
int dis[maxn],top[maxn],son[maxn];
int q,tidnum;
int pos[maxn],tid[maxn];
struct node{
int to,next;
}edge[maxn<<2];
struct Node{
int left,right,flag,sum;
}tree[maxn<<2];
void addo(int u,int v){
edge[++k].to = v;
edge[k].next = head[u];
head[u] = k;
}
void dfs1(int x,int fa,int depth){
size[x] = 1,father[x] = fa,deep[x] = depth;
for(int i = head[x];i;i = edge[i].next){
if(edge[i].to == fa) continue;
dfs1(edge[i].to,x,depth+1);
size[x] += size[edge[i].to];
if(!son[x] || size[edge[i].to] > size[son[x]]) son[x] = edge[i].to;
}
}
void dfs2(int x,int tp){
tid[x] = ++tidnum;
pos[tid[x]] = x;
top[x] = tp;
if(!son[x]) return;
dfs2(son[x],tp);
for(int i = head[x];i;i = edge[i].next){
if(edge[i].to != son[x] && edge[i].to != father[x]){
dfs2(edge[i].to,edge[i].to);
}
}
}
inline int ls(int x){return x<<1;};
inline int rs(int x){return x<<1|1;};
void build(int id,int l,int r){
tree[id].left = l;tree[id].right = r;
tree[id].sum = 0,tree[id].flag = -1;
if(l == r) return;
int mid = (l + r) >> 1;
build(ls(id),l,mid),build(rs(id),mid+1,r);
return;
}
void downdata(int id)
{
tree[id<<1].sum=(tree[id<<1].right-tree[id<<1].left+1)*tree[id].flag;
tree[id<<1|1].sum=(tree[id<<1|1].right-tree[id<<1|1].left+1)*tree[id].flag;
tree[id<<1].flag=tree[id<<1|1].flag=tree[id].flag;
tree[id].flag=-1;
}
int get(int id,int l,int r)
{
if(tree[id].right<l||tree[id].left>r) return 0;
if(tree[id].right<=r&&tree[id].left>=l) return tree[id].sum;
if(tree[id].flag!=-1) downdata(id);
return get(id<<1,l,r)+get(id<<1|1,l,r);
}
void update(int id,int l,int r,int val)
{
if(tree[id].right<l||tree[id].left>r) return;
if(tree[id].right<=r&&tree[id].left>=l)
{
tree[id].sum=(tree[id].right-tree[id].left+1)*val;
tree[id].flag=val;
return;
}
if(tree[id].flag!=-1) downdata(id);
update(id<<1,l,r,val);update(id<<1|1,l,r,val);
tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
return;
}
void change(int u,int v,int val)
{
while(top[u]!=top[v])
{
if(deep[top[u]]<deep[top[v]]) std::swap(u,v);
update(1,tid[top[u]],tid[u],val);
u=father[top[u]];
}
if(deep[u]>deep[v]) std::swap(u,v);
update(1,tid[u],tid[v],val);
return;
}
int main(){
ios::sync_with_stdio(0);
cin>>n;
for(int i = 2;i <= n;i++){
cin>>x;
x++;
addo(x,i);
}
dfs1(1,1,1),dfs2(1,1);
cin>>q;
build(1,1,tidnum);
while(q --> 0){
string s;
cin>>s>>x;
x++;
int t1 = tree[1].sum;
if(s[0] == 'i'){
change(1,x,1);
int t2 = tree[1].sum;
cout<<abs(t2 - t1)<<endl;
}
else{
update(1,tid[x],tid[x] + size[x] - 1,0);
int t2 = tree[1].sum;
cout<<abs(t1 - t2)<<endl;
}
}
return 0;
}