郑州寄 8.9
前言
业精于勤荒于嬉,行成于思毁于随
正文(加餐)
树论选讲,难绷
关键词:路径信息、批量处理、树上数据结构
好耶,没有模拟赛!今天是快乐的讲题日!
???怎么又讲 LCA 和 RMQ,耳朵都快听出茧子了
电脑坏了,难绷
\(8:30\) 拾取了一台新电脑
啊啊啊啊啊啊啊啊嘴好笨,好不容易秒一道蓝题,上去讲题结果老师没听懂呐,台下好多人,一个 I 人静静地碎掉了
换了个人上来讲,明明和我说的是一个东西……
吐槽时间结束
(不对,还不能结束,墨鱼怎么又和软软双宿双飞了,怎么趁我破碎的时候秒了四个题???)
T1
?老师,您礼貌吗?早八放个紫题当开胃菜啊???
哦,倍增 LCA + 线性基板子,那没事了
题意
题解
在若干数中任选,要求异或和最大,容易想到线性基
注意到线性基不可差分而可合并,所以考虑倍增
具体地,记 fp[i][j] 表示从 \(i\) 结点出发向上跳 \(2^j\) 步所获得的所有线性基
然后暴力合并即可,时间复杂度 \(O(n \log n \times \log V)\)
LCA 就不多说了……
代码
已然不会默写线性基板子咯!
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define vi vector<int>
#define pb push_back
using namespace std;
const int N=2e4+5,L=65;
int n,m,a[N],fa[N][16],dep[N];vi G[N];
struct LB{
int p[L];
LB(){memset(p,0,sizeof(p));}
inline void ins(int x){
for(int i=63;i>=0;i--){
if(!((x>>i)&1))continue;
if(!p[i]){p[i]=x;return;}
else x^=p[i];
}
return;
}
inline int qry(){
int res=0;
for(int i=63;i>=0;i--)res=max(res,res^p[i]);
return res;
}
}fp[N][16];
inline void dfs(int u,int fath){
fa[u][0]=fath;fp[u][0].ins(a[fath]);
dep[u]=dep[fath]+1;
for(int v:G[u])
if(v!=fath)dfs(v,u);
return;
}
inline LB merge(LB x,LB y){
for(int i=63;i>=0;i--)
if(y.p[i])x.ins(y.p[i]);
return x;
}
inline int ask(int x,int y){
if(dep[x]>dep[y])swap(x,y);
LB res;
res.ins(a[x]),res.ins(a[y]);
for(int i=15;i>=0;i--)
if(dep[fa[y][i]]>=dep[x]){
res=merge(res,fp[y][i]);
y=fa[y][i];
}
if(x==y)return res.qry();
for(int i=15;i>=0;i--)
if(fa[x][i]!=fa[y][i]){
res=merge(res,fp[x][i]);
res=merge(res,fp[y][i]);
x=fa[x][i],y=fa[y][i];
}
res=merge(res,fp[x][0]);
return res.qry();
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n-1;i++){
int u,v;cin>>u>>v;
G[u].pb(v),G[v].pb(u);
}
dfs(1,0);
for(int j=1;j<=15;j++){
for(int i=1;i<=n;i++){
fa[i][j]=fa[fa[i][j-1]][j-1];
fp[i][j]=merge(fp[fa[i][j-1]][j-1],fp[i][j-1]);
}
}
while(m--){
int x,y;cin>>x>>y;
cout<<ask(x,y)<<'\n';
}
return 0;
}
T2
我讲的,我碎了(宿管举着相机在那里拍)
题意
题解
首先如果没有删除怎么做,显然可以单调栈维护 \(a_i\) 的极长最值段
现在把删除操作加进来,假设删除的元素是 \(a_i\),预处理删除一个元素给答案带来的变化量
(这不是随便做吗,感觉码量不会超过 \(100\) 行)
代码
撤回了一个随便做
死因:ans[i]=ans[i-1]+d[i-1]
间接死因:多测清空并不完整
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int n,a[N],stk[N],tp,L[N],R[N],ans[N],d[N];
struct ST_list{
int lg[N],f[N][21];
inline void init(){
lg[0]=-1;
for(int i=1;i<N;i++)lg[i]=lg[i>>1]+1;
return;
}
inline void work(){
for(int i=1;i<=n;i++)f[i][0]=a[i];
for(int j=1;j<=lg[n];j++)
for(int i=1;i<=n-(1<<j)+1;i++)
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
return;
}
inline int ask(int l,int r){
int k=lg[r-l+1];
return min(f[l][k],f[r-(1<<k)+1][k]);
}
}S;
inline void upd(int l,int r,int v){
if(l<=r)d[l]+=v,d[r+1]-=v;
return;
}
inline void solve(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)L[i]=0,R[i]=n+1;
for(int i=1;i<=n;i++){
while(tp&&a[stk[tp]]>a[i])R[stk[tp--]]=i;
L[i]=stk[tp];stk[++tp]=i;
}
// for(int i=1;i<=n;i++)cerr<<L[i]<<" "<<R[i]<<endl;
for(int i=1;i<=n;i++){
upd(1,L[i]-1,a[i]*(i-L[i])*(R[i]-i));
upd(R[i]+1,n,a[i]*(i-L[i])*(R[i]-i));
upd(L[i]+1,i-1,a[i]*(i-L[i]-1)*(R[i]-i));
upd(i+1,R[i]-1,a[i]*(i-L[i])*(R[i]-i-1));
}
for(int i=1;i<=n;i++)ans[i]=ans[i-1]+d[i];
S.work();
for(int i=1;i<=n;i++){
int l=1,r=L[i]-1,o=0;
while(l<=r){
int mid=(l+r)>>1;
if(S.ask(mid,L[i]-1)>a[i])r=mid-1,o=L[i]-mid;
else l=mid+1;
}
o+=i-L[i];
ans[L[i]]+=a[i]*o*(R[i]-i);
}
for(int i=1;i<=n;i++){
int l=R[i]+1,r=n,o=0;
while(l<=r){
int mid=(l+r)>>1;
if(S.ask(R[i]+1,mid)>a[i])l=mid+1,o=mid-R[i];
else r=mid-1;
}
o+=R[i]-i;
ans[R[i]]+=a[i]*(i-L[i])*o;
}
for(int i=1;i<=n;i++)cout<<ans[i]<<' ';
cout<<'\n';
return;
}
inline void clr(){
for(int i=0;i<=n+1;i++)ans[i]=d[i]=0;
for(int i=1;i<=tp;i++)stk[i]=0;
tp=0;
return;
}
signed main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
S.init();
int T;cin>>T;while(T--)solve(),clr();
return 0;
}
/*
4
1
1
3
3 1 2
5
4 2 1 5 3
8
8 1 4 6 7 3 5 2
*/
T3
简单题
题意
题解
容易发现,题目条件等价于两个序列的笛卡尔树形态相同,所以等价于对笛卡尔树进行一个计数
记 \(f_{u,i}\) 表示 \(u\) 结点权值为 \(i\) 的方案数,转移方程形如:
代码
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define vi vector<int>
using namespace std;
const int N=1e6+5,MOD=1e9+7;
int n,m,a[N],stk[N],cnt,lc[N],rc[N];
vector<vi> f;
inline void ADD(int &x,int y){
x%=MOD;y%=MOD;x=(x+y)%MOD;
return;
}
inline void MUL(int &x,int y){
x%=MOD;y%=MOD;x=x*y%MOD;
return;
}
inline void dfs(int u){
for(int i=1;i<=m;i++)f[u][i]=1;
if(lc[u])dfs(lc[u]);
if(rc[u])dfs(rc[u]);
if(lc[u]){
for(int i=1;i<=m;i++)MUL(f[u][i],f[lc[u]][i-1]);
}
if(rc[u]){
for(int i=1;i<=m;i++)MUL(f[u][i],f[rc[u]][i]);
}
for(int i=2;i<=m;i++)ADD(f[u][i],f[u][i-1]);
return;
}
inline void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++){
int tp=cnt;
while(tp&&a[stk[tp]]<a[i])tp--;
if(tp)rc[stk[tp]]=i;
if(tp<cnt)lc[i]=stk[tp+1];
stk[cnt=++tp]=i;
}
// for(int i=1;i<=n;i++)cerr<<lc[i]<<' '<<rc[i]<<endl;
f.resize(n+1);
for(int i=1;i<=n;i++)f[i].resize(m+1);
dfs(stk[1]);
cout<<f[stk[1]][m]<<'\n';
return;
}
inline void clr(){
for(int i=1;i<=n;i++)lc[i]=rc[i]=0;
for(int i=1;i<=cnt;i++)stk[i]=0;
cnt=0;
return;
}
signed main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T;cin>>T;while(T--)solve(),clr();
return 0;
}
/*
4
3 3
1 3 2
4 2
2 2 2 2
6 9
6 9 6 9 6 9
9 100
10 40 20 20 100 60 80 60 60
*/
T4
树剖板子题的弱化版
T5
树剖板子题的弱化版 \(\times 2\)
T6
原题,经典好题,传说中的轻重边
贴个曾经的题解 链接 吧!
T7
墨鱼:随便 DSU 就行了
云落:???明明线段树合并
墨鱼不语,只是一味地敲代码
云落屈服了,写了 DSU
T8-T10086
写不完,根本写不完,挖个天坑,以后大概率不会补了
后记
世界孤立我任它奚落
完结撒花!

浙公网安备 33010602011771号