放一些比较常见的数据结构处理技巧,会一点一点补上来。
P3313 [SDOI2014]旅行
给你一个 \(10^5\) 长的序列,每个点有颜色 \(c\) 和权值 \(v\)。
有修改和查询操作,修改可以为修改一个点的颜色或权值,查询一段区间内颜色为 \(c\) 的点的点权最大值以及权值和。
$\texttt{solution}$
发现直接对于每个颜色开一个动态开点线段树就做完了。
每次操作最多只会建立 \(\log n\) 个节点,所以复杂度也是对滴。
原题只不过讲上面的操作放在了树上,我直接莽一个树剖上去就做完了。
嘴巴完跑路。
P2056 [ZJOI2007]捉迷藏
单点修改,维护全局最大、次大值。
\(n\le 2\times 10^5\)
$\texttt{solution}$
一种常数超级大的想法是用 muiltiset
,但是不建议尝试。
我们可以维护两个队,分别维护存在的与不存在的值。
如果加入一个值,就向第一个堆中添加元素;如果删除一个值,就向第二个堆中添加元素。
查询的时候如果两个堆中的元素相同,就同时 pop
,最后可以留下需要的值。
原题就是这个技巧反复运用再套上氡态淀粉质啦!
彩虹路径
一棵树上每条边可以选择一些颜色,每条边可以选择的颜色给定,定义一条路径的美观度为:路径上相邻两条边且颜色不同的边的对数。查询从 \(u\) 到 \(v\) 路径上的美观度最大为多少。
\(n\le 10^5\)
$\texttt{solution}$
\(\bigstar\):发现如果一条边的颜色数量大于 \(3\),那么一定存在对于所有询问的一组最优解,这条边仅仅使用了三种颜色。
那么可以随机选择三种颜色作为这条边的备选颜色,之后倍增 dp 乱跑即可。
$\texttt{code}$
#define Maxn 100005
#define Maxm 300005
#define Maxpown 22
int n,m,q,tot;
int hea[Maxn],nex[Maxm<<1],ver[Maxm<<1],edg[Maxm<<1];
int hav[Maxn],col[Maxn][3],fa[Maxpown][Maxn],dep[Maxn];
struct DP
{
int dp[3][3],ucol[3],vcol[3];
inline void init()
{
ucol[0]=ucol[1]=ucol[2]=0;
vcol[0]=vcol[1]=vcol[2]=0;
for(int i=0;i<3;i++) for(int j=0;j<3;j++)
dp[i][j]=-inf;
}
inline int query()
{
int maxx=-inf;
for(int i=0;i<3;i++) for(int j=0;j<3;j++)
maxx=max(maxx,dp[i][j]);
return maxx;
}
};
DP a[Maxpown][Maxn];
inline DP merge(DP x,DP y)
{ // xl - xr - yl - yr 相接
if(!x.ucol[0]) return y;
if(!y.ucol[0]) return x;
DP ret; ret.init();
for(int lx=0;lx<3;lx++) ret.ucol[lx]=x.ucol[lx];
for(int ry=0;ry<3;ry++) ret.vcol[ry]=y.vcol[ry];
for(int lx=0;lx<3;lx++) for(int ry=0;ry<3;ry++)
{
if(!x.ucol[lx] || !y.vcol[ry]) continue;
for(int rx=0;rx<3;rx++) for(int ly=0;ly<3;ly++)
{
if(!x.vcol[rx] || !y.ucol[ly]) continue;
ret.dp[lx][ry]=max(ret.dp[lx][ry],
x.dp[lx][rx]+y.dp[ly][ry]+
((x.vcol[rx]!=y.ucol[ly])?1:0));
}
}
return ret;
}
bool vis[Maxn];
inline void add(int x,int y,int d)
{ ver[++tot]=y,nex[tot]=hea[x],hea[x]=tot,edg[tot]=d; }
void Add(int x,int val)
{
if(hav[x]==3) return;
bool p=false;
for(int i=0;i<hav[x];i++) if(col[x][i]==val) p=true;
if(!p) col[x][hav[x]++]=val;
}
void dfs(int x,int F)
{
vis[x]=true;
for(int i=hea[x];i;i=nex[i])
{
if(ver[i]==F) continue;
Add(ver[i],edg[i]);
if(vis[ver[i]]) continue;
fa[0][ver[i]]=x,dep[ver[i]]=dep[x]+1;
for(int j=1;j<=20;j++)
fa[j][ver[i]]=fa[j-1][fa[j-1][ver[i]]];
dfs(ver[i],x);
}
}
inline int query(int x,int y)
{
if(x==y) return 0;
DP L,R; L.init(),R.init();
if(dep[x]>dep[y]) swap(x,y);
for(int i=20;i>=0;i--) if(dep[fa[i][y]]>=dep[x])
R=merge(a[i][y],R),y=fa[i][y];
if(x==y) return R.query();
for(int i=20;i>=0;i--) if(fa[i][x]!=fa[i][y])
L=merge(a[i][x],L),x=fa[i][x],
R=merge(a[i][y],R),y=fa[i][y];
L=merge(a[0][x],L),R=merge(a[0][y],R);
int ret=-inf;
for(int rx=0;rx<3;rx++) for(int ry=0;ry<3;ry++)
{
if(!L.vcol[rx] || !R.vcol[ry]) continue;
for(int lx=0;lx<3;lx++) for(int ly=0;ly<3;ly++)
{
if(!L.ucol[lx] || !R.ucol[ly]) continue;
ret=max(ret,L.dp[lx][rx]+R.dp[ly][ry]+
((L.ucol[lx]!=R.ucol[ly])?1:0));
}
}
return ret;
}
int main()
{
n=rd(),m=rd();
for(int i=1,x,y,d;i<=m;i++)
x=rd(),y=rd(),d=rd(),add(x,y,d),add(y,x,d);
dep[1]=1,dfs(1,0);
for(int i=0;i<=20;i++) for(int j=0;j<=n;j++)
a[i][j].init();
for(int i=2;i<=n;i++) for(int j=0;j<hav[i];j++)
{
a[0][i].dp[j][j]=0,
a[0][i].ucol[j]=a[0][i].vcol[j]=col[i][j];
}
for(int i=1;i<=20;i++)
for(int j=2;j<=n;j++)
a[i][j]=merge(a[i-1][fa[i-1][j]],a[i-1][j]);
q=rd();
for(int i=1,u,v;i<=q;i++)
u=rd(),v=rd(),printf("%d\n",query(u,v));
return 0;
}