高一上一月下旬日记
1.21
闲话
- 上午通过一些手段把课补完了。
- 临吃午饭时
过来和我们说 刚给他打电话说让我们空着手去图书馆一楼,他也不知道让我们去干什么。到了之后发现班主任正在分发各奥赛的寒假作业,然后就被拉去当苦力了,并参观了下因教室占了备课区导致备课区都搬到了图书馆二楼原阅览室,老师多用的是自己的笔记本电脑、平板电脑,部分台式电脑的显示屏较小。 - 寒假作业试卷、答题卡、作业一共两本,整个寒假有
天需要写假期作业,大概一天写两科,周日不用写作业,年假放两天。啥时候普通学生寒假都这么短了。 - 下午起床后把教室遗留的语文、英语资料也搬到机房了。教室里只剩下了生物、信息两科奥赛,遂讲评取消了。
左右 领着一个 的学长来机房给我们做宣讲。自我介绍时说他当时只有一个省二,而且 成绩也不好,经过高三一轮、二轮复习后考到了南开大学,在 区域赛也拿了几块银牌,现在负责南开大学 校队的管理工作, 说他的说法太谦虚了。同时提到让我们不要太担心现在的 成绩,现在远不如高三一轮、二轮复习重要。讲了下一些高校对算法竞赛的政策,包括但不限于 区域赛拿银牌以上本校基本就确定保研了,就算转到其他高校或者出国也有很高的含金量;以南开大学为例,从华五到中流 省二以上基本可以随便转专业,还举了一个在华科大上学的学长报考最低分专业后成功转专业的例子;强基不能转专业,且目前高校给出的政策以数学居多,但研究生的时候就不管了。然后讲了下他的 经历,说我们现在学算法竞赛在高中时期可能不太显眼,但到大学后包括代码实践课在内的算法竞赛相关领域基本可以吊打大学才开始学的零基础选手,现在的知识储备放到大学拿银牌会比较轻松,举了某个学长带着一个省一和一个零基础的打到了 的银牌的例子。还问了下我们怎么 在 个节点的树上求出 个关键点的 。然后说了下与信息奥赛相关的专业,以计科、网络安全、数学为主。又说了下想保研要么走竞赛(算法竞赛、 等),要么就走科研,本科生去准备研究生的工作。最后给我们鼓了鼓劲, 说要珍惜与学长交流的机房,高校校园行时来宣讲最多讲讲师资力量、科研成果,大部分会与我们无关,但学长讲的都是实打实的经验。 还强调说要给自己确立一个明确的目标,不要像之前有人写“感觉上学就是为了上个好大学”,要为自己找到坚持学习下去的意义。 说寒假集训从今天就算是正常开始了;他打算开始落实每人在晚新闻分享题目的制度,题目大部分人都做过也没太大关系;后面可能会给我们安排打互测胡测,一个人组或者几个人组都可以,这也会作为给别人分享题目的手段之一。
做题纪要
CF893F Subtree Minimum Query
-
在深度和
序上二维数点,因维护信息的特殊性使用主席树维护。点击查看代码
struct node { int nxt,to; }e[200010]; int head[100010],dep[100010],a[100010],dfn[100010],out[100010],cnt=0,tot=0; vector<int>pos[100010]; void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(int x,int fa) { tot++; dfn[x]=tot; dep[x]=dep[fa]+1; pos[dep[x]].push_back(x); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa) dfs(e[i].to,x); } out[x]=tot; } struct PDS_SMT { int root[100010],rt_sum=0; struct SegmentTree { int ls,rs,minn; }tree[100010<<5]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) int build_rt() { rt_sum++; lson(rt_sum)=rson(rt_sum)=0; tree[rt_sum].minn=0x7f7f7f7f; return rt_sum; } void update(int pre,int &rt,int l,int r,int pos,int val) { rt=build_rt(); tree[rt]=tree[pre]; tree[rt].minn=min(tree[rt].minn,val); if(l==r) return; int mid=(l+r)/2; if(pos<=mid) update(lson(pre),lson(rt),l,mid,pos,val); else update(rson(pre),rson(rt),mid+1,r,pos,val); } int query(int rt,int l,int r,int x,int y) { if(rt==0) return 0x7f7f7f7f; if(x<=l&&r<=y) return tree[rt].minn; int mid=(l+r)/2; if(y<=mid) return query(lson(rt),l,mid,x,y); if(x>mid) return query(rson(rt),mid+1,r,x,y); return min(query(lson(rt),l,mid,x,y),query(rson(rt),mid+1,r,x,y)); } }T; int main() { //#define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,rt,m,u,v,ans=0,i,j; cin>>n>>rt; for(i=1;i<=n;i++) cin>>a[i]; for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } dfs(rt,0); T.tree[0].minn=0x7f7f7f7f; for(i=1;i<=n;i++) { T.root[i]=T.root[i-1]; for(j=0;j<pos[i].size();j++) T.update(T.root[i],T.root[i],1,n,dfn[pos[i][j]],a[pos[i][j]]); } cin>>m; for(i=1;i<=m;i++) { cin>>u>>v; u=(u+ans)%n+1; v=(v+ans)%n; cout<<(ans=T.query(T.root[min(dep[u]+v,n)],1,n,dfn[u],out[u]))<<endl; } return 0; }
CF494D Birthday
[ARC101E] Ribbons on Tree
1.22
闲话
- 上午
打学校 的模拟赛。刚开始时 给我们发了周黑鸭吃。 - 临吃午饭时发现学校组织了校友回母校活动,顺便作为优秀毕业生给高三学生宣讲高校。
- 下午讲题。
- 临吃晚饭时
说寒假集训期间改为去西扩食堂吃饭,考虑到我们不搬宿舍等情况早中晚吃饭时间分别为 。去吃晚饭的路上以为高三也放假了,遂慢慢悠悠地走,到了食堂后发现一大堆高三的从楼上冲了下来,然后就跑餐了。 - 吃完晚饭回来
问我们和高三相比谁吃饭更早,我们说是公平竞争。 - 晚上讲题。
- 晚上回宿舍后班主任挨个宿舍通知明天正常跑早操,集合地点不变,说假期期间让我们也锻炼锻炼。
做题纪要
luogu P6845 [CEOI2019] Dynamic Diameter
-
多倍经验: CF1192B Dynamic Diameter
-
边权转点权。
-
线段树维护
序上区间内点集的直径,由点集合并时直径端点的结论辅助进行pushup
。 -
修改边权仅对子树内部的点有影响,递归至对应区间后往上进行
pushup
即可。点击查看代码
struct node { ll nxt,to; }e[200010]; ll head[100010],fa[100010],siz[100010],dep[100010],son[100010],top[100010],pos[100010],dfn[100010],out[100010],u[100010],v[100010],w[100010],tmp[5],cnt=0,tot=0; void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(ll x,ll father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(ll x,ll id) { top[x]=id; tot++; dfn[x]=tot; pos[tot]=x; if(son[x]!=0) { dfs2(son[x],id); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa[x]&&e[i].to!=son[x]) dfs2(e[i].to,e[i].to); } } out[x]=tot; } struct BIT { ll c[100010]; ll lowbit(ll x) { return (x&(-x)); } void add(ll n,ll x,ll val) { for(ll i=x;i<=n;i+=lowbit(i)) c[i]+=val; } void update(ll n,ll l,ll r,ll val) { add(n,l,val); add(n,r+1,-val); } ll getsum(ll x) { ll ans=0; for(ll i=x;i>=1;i-=lowbit(i)) ans+=c[i]; return ans; } }B; ll lca(ll u,ll v) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) u=fa[top[u]]; else v=fa[top[v]]; } return dep[u]<dep[v]?u:v; } ll get_dis(ll x,ll y) { return B.getsum(dfn[x])+B.getsum(dfn[y])-2*B.getsum(dfn[lca(x,y)]); } struct SMT { struct SegmentTree { ll u,v; }tree[400010]; #define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) void pushup(ll rt) { ll maxx=0; tmp[1]=tree[lson(rt)].u; tmp[2]=tree[lson(rt)].v; tmp[3]=tree[rson(rt)].u; tmp[4]=tree[rson(rt)].v; for(ll i=1;i<=4;i++) { for(ll j=i+1;j<=4;j++) { if(get_dis(tmp[i],tmp[j])>maxx) { maxx=get_dis(tmp[i],tmp[j]); tree[rt].u=tmp[i]; tree[rt].v=tmp[j]; } } } } void build(ll rt,ll l,ll r) { if(l==r) { tree[rt].u=tree[rt].v=pos[l]; return; } ll mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void update(ll rt,ll l,ll r,ll x,ll y) { if(x<=l&&r<=y) return; ll mid=(l+r)/2; if(x<=mid) update(lson(rt),l,mid,x,y); if(y>mid) update(rson(rt),mid+1,r,x,y); pushup(rt); } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,mod,x,y,ans=0,i; cin>>n>>m>>mod; for(i=1;i<=n-1;i++) { cin>>u[i]>>v[i]>>w[i]; add(u[i],v[i]); add(v[i],u[i]); } dfs1(1,0); dfs2(1,1); for(i=1;i<=n-1;i++) { if(dep[u[i]]>dep[v[i]]) B.update(n,dfn[u[i]],out[u[i]],w[i]); else B.update(n,dfn[v[i]],out[v[i]],w[i]); } T.build(1,1,n); for(i=1;i<=m;i++) { cin>>x>>y; x=(x+ans)%(n-1)+1; y=(y+ans)%mod; if(dep[u[x]]>dep[v[x]]) { B.update(n,dfn[u[x]],out[u[x]],y-w[x]); T.update(1,1,n,dfn[u[x]],out[u[x]]); } else { B.update(n,dfn[v[x]],out[v[x]],y-w[x]); T.update(1,1,n,dfn[v[x]],out[v[x]]); } w[x]=y; cout<<(ans=get_dis(T.tree[1].u,T.tree[1].v))<<endl; } return 0; }
luogu P11266 【模板】可并堆 2
-
多倍经验: luogu P3377 【模板】左偏树/可并堆 | luogu P1456 Monkey King | luogu P2713 罗马游戏
-
左偏树板子。
- 左偏树是一种可并堆,具有堆的性质,且支持快速合并。
- 对于一棵二叉树,定义 外节点 为子节点数
个的节点;定义一个节点的 值为其到子树内最近的 外节点 所经过的点的数量,特别地,有空节点的 。 不等价于深度;左偏树的深度没有保证,例如一条向左的链也是一棵左偏树。故常使用路径压缩并查集辅助查询某个元素所在左偏树的最大/小值。
- 左偏树是一棵二叉树,不仅具有堆的性质,而且是 左偏 的,即每个节点左儿子的
值都 右儿子的 值。故每个节点的 值都等于右儿子的 值 。 - 以下部分以小根堆为例。
merge
- 合并两棵根节点分别为的左偏树
时,取根节点值较小的左偏树的根节点作为合并后的根节点。以 的值较小为例,接着 的左子树不变,将 递归合并到 的右子树上去。合并后,为满足 左偏 性质若左儿子的 值 右儿子的 值则交换左右儿子。 - 由左偏性质,每递归一层其中一个左偏树根节点的
值就会减少 ,又因为一棵根的 值等于 的二叉树至少有 层满二叉树,可知合并的时间复杂度为 。
- 合并两棵根节点分别为的左偏树
insert
- 将新插入的节点也看做是一棵左偏树,与原树进行合并。
del
- 将要删除的节点的左右子树进行合并,然后自底而上更新
值直至 值无需更新,中途需要满足 左偏 性质。 - 时间复杂度为
。
- 将要删除的节点的左右子树进行合并,然后自底而上更新
pushup
- 自底而上更新
值需要额外记录父亲节点(特别地,定义根节点的父亲节点为自己)。
- 自底而上更新
- 为方便查询最小值,可以对每棵左偏树记录一下根节点,上述操作过程中辅助维护。
点击查看代码
int a[1000010]; struct Heap { int root[1000010]; struct Leftist_Tree { int ls,rs,val,fa,d; }tree[1000010]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) #define fa(rt) (tree[rt].fa) void build_rt(int rt,int val) { lson(rt)=rson(rt)=0; tree[rt].val=val; tree[rt].fa=rt; tree[rt].d=1; } int merge(int rt1,int rt2) { if(rt1==0||rt2==0) return rt1+rt2; if(tree[rt2].val<tree[rt1].val) swap(rt1,rt2); rson(rt1)=merge(rson(rt1),rt2); if(tree[lson(rt1)].d<tree[rson(rt1)].d) swap(lson(rt1),rson(rt1));//不满足左偏性质,需要交换 fa(lson(rt1))=fa(rson(rt1))=fa(rt1)=rt1;//更新父亲节点 tree[rt1].d=tree[rson(rt1)].d+1; return rt1; } void pushup(int rt) { if(rt==0) return; if(tree[lson(rt)].d<tree[rson(rt)].d) swap(lson(rt),rson(rt));//不满足左偏性质,需要交换 if(tree[rt].d!=tree[rson(rt)].d+1) { tree[rt].d=tree[rson(rt)].d+1; pushup(fa(rt)); } } void del(int &rt,int pos) { int x=merge(lson(pos),rson(pos)); if(rt==pos) rt=fa(x)=x; else { if(lson(fa(pos))==pos) lson(fa(pos))=x; if(rson(fa(pos))==pos) rson(fa(pos))=x; fa(x)=fa(pos); pushup(fa(x)); } } int query(int rt) { return tree[rt].val; } void update(int &rt,int pos,int val)//将修改操作转化为先删除,再添加 { del(rt,pos); build_rt(pos,val); rt=merge(rt,pos); } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,pd,x,y,z,i; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) { scanf("%d",&a[i]); T.root[i]=i; T.build_rt(i,a[i]); } for(i=1;i<=m;i++) { scanf("%d%d",&pd,&x); if(pd==0) { scanf("%d",&y); T.del(T.root[x],y); } if(pd==1) printf("%d\n",T.query(T.root[x])); if(pd==2) { scanf("%d",&y); T.root[x]=T.merge(T.root[x],T.root[y]); } if(pd==3) { scanf("%d%d",&y,&z); T.update(T.root[x],y,z); } } return 0; }
HZTG5837. 山河入梦来
HZTG5836. 小幸运
1.23
闲话
- 早起时没有起床铃,但部分宿舍的灯照常自己亮了。候操时等待了较长时间才等来老师组织开始跑操,仅跑一圈。
- 上午
打 accoders NOI 的模拟赛。 - 中午吃饭时碰见了 @xuany , @chancelong , @APJifengc , @Sonnety 。
- 下午放 @Estelle_N 的每日一歌《如愿》;体育课通过和
一番交涉获得胜利,让我们在 前回到机房,下楼的时候 说别上完体育课人就没了,因排球馆里没人遂直接进去打羽毛球了,打到一半被某个老师说他要锁门了而赶了出来。 - 下午高三的放假了。
- 晚上讲题。
做题纪要
luogu P4971 断罪者
-
左偏树删除任意元素的另一种写法是将左右子树和上方的部分分别合并起来,注意左右儿子的清空。
-
并查集辅助维护。
点击查看代码
ll a[2000010]; struct Heap { ll fa[2000010]; struct Leftist_Tree { ll ls,rs,val,d; }tree[2000010]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) void build_rt(ll rt,ll val) { lson(rt)=rson(rt)=0; tree[rt].val=val; fa[rt]=rt; tree[rt].d=1; } ll find(ll x) { return fa[x]==x?x:fa[x]=find(fa[x]); } ll merge(ll rt1,ll rt2) { if(rt1==0||rt2==0) return rt1+rt2; if((tree[rt2].val>tree[rt1].val)||(tree[rt2].val==tree[rt1].val&&rt2<rt1)) swap(rt1,rt2); rson(rt1)=merge(rson(rt1),rt2); if(tree[lson(rt1)].d<tree[rson(rt1)].d) swap(lson(rt1),rson(rt1)); fa[lson(rt1)]=fa[rson(rt1)]=fa[rt1]=rt1; tree[rt1].d=tree[rson(rt1)].d+1; return rt1; } ll query(ll rt) { return tree[rt].val; } void update(ll rt,ll val) { ll x=merge(lson(rt),rson(rt)); build_rt(rt,max(tree[rt].val-val,0ll)); fa[rt]=fa[x]=merge(rt,x); } void del(ll pos) { ll ls=lson(pos),rs=rson(pos); lson(pos)=rson(pos)=tree[pos].val=0; merge(merge(ls,rs),find(pos)); } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll t,w,k,n,m,ans,maxx,pd,x,y,i; scanf("%lld%lld%lld",&t,&w,&k); for(;t>=1;t--) { scanf("%lld%lld",&n,&m); ans=maxx=0; for(i=1;i<=n;i++) { scanf("%lld",&a[i]); T.build_rt(i,a[i]); } for(i=1;i<=m;i++) { scanf("%lld%lld",&pd,&x); if(pd==2) T.del(x); if(pd==3) { scanf("%lld",&y); x=T.find(x); T.update(x,y); } if(pd==4) { scanf("%lld",&y); x=T.find(x); y=T.find(y); if(x!=y) T.fa[x]=T.fa[y]=T.merge(x,y); } } for(i=1;i<=n;i++) { if(T.fa[i]==i) { ans+=T.query(i); maxx=max(maxx,T.query(i)); } } if(w==2) ans-=maxx; if(w==3) ans+=maxx; if(ans==0) printf("Gensokyo 0\n"); else if(ans>k) printf("Hell %lld\n",ans); else printf("Heaven %lld\n",ans); } return 0; }
luogu P1552 [APIO2012] 派遣
-
建大根堆存储堆内元素的和,左偏树合并时保证总和
。点击查看代码
struct node { ll nxt,to; }e[100010]; ll head[100010],fa[100010],c[100010],l[100010],cnt=0,m,ans=0; void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } struct Heap { ll root[100010]; struct Leftist_Tree { ll ls,rs,val,d,siz,sum; }tree[100010]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) void build_rt(ll rt,ll val) { lson(rt)=rson(rt)=0; tree[rt].val=tree[rt].sum=val; tree[rt].d=tree[rt].siz=1; } void pushup(ll rt) { tree[rt].d=tree[rson(rt)].d+1; tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+1; tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum+tree[rt].val; } ll merge(ll rt1,ll rt2) { if(rt1==0||rt2==0) return rt1+rt2; if(tree[rt1].val<tree[rt2].val) swap(rt1,rt2); rson(rt1)=merge(rson(rt1),rt2); if(tree[lson(rt1)].d<tree[rson(rt1)].d) swap(lson(rt1),rson(rt1)); pushup(rt1); return rt1; } void pop(ll &rt) { rt=merge(lson(rt),rson(rt)); } ll top(ll rt) { return tree[rt].sum; } ll query(ll rt) { return tree[rt].siz; } }T; void dfs(ll x) { T.root[x]=x; T.build_rt(x,c[x]); while(T.root[x]!=0&&T.top(T.root[x])>m) T.pop(T.root[x]); for(ll i=head[x];i!=0;i=e[i].nxt) { dfs(e[i].to); T.root[x]=T.merge(T.root[x],T.root[e[i].to]); while(T.root[x]!=0&&T.top(T.root[x])>m) T.pop(T.root[x]); } ans=max(ans,T.query(T.root[x])*l[x]); } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>fa[i]>>c[i]>>l[i]; if(i!=1) add(fa[i],i); } dfs(1); cout<<ans<<endl; return 0; }
luogu P3261 [JLOI2015] 城池攻占
-
合并和弹出时先下传懒惰标记。
点击查看代码
struct node { ll nxt,to; }e[300010]; ll head[300010],fa[300010],dep[300010],a[300010],v[300010],h[300010],s[300010],c[300010],ans[2][300010],cnt=0; vector<ll>f[300010]; void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } struct Heap { ll root[300010]; struct Leftist_Tree { ll ls,rs,val,d,add,mul; }tree[300010]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) void build_rt(ll rt,ll val) { lson(rt)=rson(rt)=tree[rt].add=0; tree[rt].val=val; tree[rt].d=tree[rt].mul=1; } void pushlazy(ll rt,ll add,ll mul) { tree[rt].val=tree[rt].val*mul+add; tree[rt].add=tree[rt].add*mul+add; tree[rt].mul=tree[rt].mul*mul; } void pushdown(ll rt) { pushlazy(lson(rt),tree[rt].add,tree[rt].mul); pushlazy(rson(rt),tree[rt].add,tree[rt].mul); tree[rt].add=0; tree[rt].mul=1; } ll merge(ll rt1,ll rt2) { if(rt1==0||rt2==0) return rt1+rt2; pushdown(rt1); pushdown(rt2); if(tree[rt1].val>tree[rt2].val) swap(rt1,rt2); rson(rt1)=merge(rson(rt1),rt2); if(tree[lson(rt1)].d<tree[rson(rt1)].d) swap(lson(rt1),rson(rt1)); tree[rt1].d=tree[rson(rt1)].d+1; return rt1; } ll top(ll rt) { return tree[rt].val; } void pop(ll &rt) { pushdown(rt); rt=merge(lson(rt),rson(rt)); } }T; void dfs(ll x) { dep[x]=dep[fa[x]]+1; for(ll i=0;i<f[x].size();i++) { T.build_rt(f[x][i],s[f[x][i]]); T.root[x]=T.merge(T.root[x],f[x][i]); } while(T.root[x]!=0&&T.top(T.root[x])<h[x]) { ans[0][x]++; ans[1][T.root[x]]=x; T.pop(T.root[x]); } for(ll i=head[x];i!=0;i=e[i].nxt) { dfs(e[i].to); T.root[x]=T.merge(T.root[x],T.root[e[i].to]); while(T.root[x]!=0&&T.top(T.root[x])<h[x]) { ans[0][x]++; ans[1][T.root[x]]=x; T.pop(T.root[x]); } } if(a[x]==0) T.pushlazy(T.root[x],v[x],1); else T.pushlazy(T.root[x],0,v[x]); } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,i; cin>>n>>m; for(i=1;i<=n;i++) cin>>h[i]; for(i=2;i<=n;i++) { cin>>fa[i]>>a[i]>>v[i]; add(fa[i],i); } for(i=1;i<=m;i++) { cin>>s[i]>>c[i]; f[c[i]].push_back(i); } dfs(1); for(i=1;i<=n;i++) cout<<ans[0][i]<<endl; for(i=1;i<=m;i++) cout<<dep[c[i]]-dep[ans[1][i]]<<endl; return 0; }
luogu P3273 [SCOI2011] 棘手的操作
-
难点在于怎么在有懒惰标记的情况下对单点进行操作。不妨只把标记放到堆顶。
-
启发式合并维护即可,将较大的堆的懒惰标记作为合并后的懒惰标记。
点击查看代码
int a[300010]; multiset<int,greater<int> >q; struct Heap { int fa[300010],siz[300010],lazy[300010]; struct Leftist_Tree { int ls,rs,val,d,fa; }tree[300010]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) #define fa(rt) (tree[rt].fa) void build_rt(int rt,int val) { lson(rt)=rson(rt)=fa(rt)=0; tree[rt].val=val; tree[rt].d=1; } int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } void pushdown(int rt,int lazy) { if(rt==0) return; tree[rt].val+=lazy; pushdown(lson(rt),lazy); pushdown(rson(rt),lazy); } int merge(int rt1,int rt2) { if(rt1==0||rt2==0) return rt1+rt2; if(tree[rt1].val<tree[rt2].val) swap(rt1,rt2); rson(rt1)=merge(rson(rt1),rt2); if(tree[lson(rt1)].d<tree[rson(rt1)].d) swap(lson(rt1),rson(rt1)); fa(lson(rt1))=fa(rson(rt1))=rt1; tree[rt1].d=tree[rson(rt1)].d+1; return rt1; } void DSU_merge(int x,int y) { if(siz[x]>siz[y]) swap(x,y); pushdown(x,lazy[x]-lazy[y]); fa[x]=fa[y]=merge(x,y); if(fa[x]==x) { q.erase(q.find(tree[y].val+lazy[y])); lazy[x]=lazy[y]; siz[x]+=siz[y]; lazy[y]=siz[y]=0; } else { q.erase(q.find(tree[x].val+lazy[y])); siz[y]+=siz[x]; lazy[x]=siz[x]=0; } } void update1(int rt,int val) { if(fa[rt]==rt) { fa(lson(rt))=fa(rson(rt))=0; int x=merge(lson(rt),rson(rt)); q.erase(q.find(tree[rt].val+lazy[rt])); build_rt(rt,tree[rt].val+val); fa[rt]=fa[x]=merge(rt,x); if(fa[rt]==x) { lazy[x]=lazy[rt]; siz[x]=siz[rt]; lazy[rt]=siz[rt]=0; q.insert(tree[x].val+lazy[x]); } else { q.insert(tree[rt].val+lazy[rt]); } } else { fa(lson(rt))=fa(rson(rt))=fa(rt); if(lson(fa(rt))==rt) lson(fa(rt))=merge(lson(rt),rson(rt)); else rson(fa(rt))=merge(lson(rt),rson(rt)); build_rt(rt,tree[rt].val+val); int x=find(rt); fa[rt]=fa[x]=merge(rt,x); if(fa[rt]==rt) { q.erase(q.find(tree[x].val+lazy[x])); q.insert(tree[rt].val+lazy[x]); lazy[rt]=lazy[x]; siz[rt]=siz[x]; lazy[x]=siz[x]=0; } } } void update2(int rt,int val) { q.erase(q.find(tree[rt].val+lazy[rt])); lazy[rt]+=val; q.insert(tree[rt].val+lazy[rt]); } int query(int rt) { return tree[rt].val; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,x,y,lazy=0,i; string pd; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; T.fa[i]=i; T.siz[i]=1; T.build_rt(i,a[i]); q.insert(a[i]); } cin>>m; for(i=1;i<=m;i++) { cin>>pd; if(pd=="U") { cin>>x>>y; x=T.find(x); y=T.find(y); if(x!=y) T.DSU_merge(x,y); } if(pd=="A1") { cin>>x>>y; T.update1(x,y); } if(pd=="A2") { cin>>x>>y; x=T.find(x); T.update2(x,y); } if(pd=="A3") { cin>>x; lazy+=x; } if(pd=="F1") { cin>>x; cout<<T.query(x)+T.lazy[T.find(x)]+lazy<<endl; } if(pd=="F2") { cin>>x; x=T.find(x); cout<<T.query(x)+T.lazy[x]+lazy<<endl; } if(pd=="F3") cout<<*q.begin()+lazy<<endl; } return 0; }
luogu P10641 BZOJ3252 攻略
-
将若干条链的交集部分在权值最大的链处计算,左偏树维护可并堆。
点击查看代码
struct node { ll nxt,to; }e[400010]; ll head[200010],a[200010],cnt=0; void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } struct Heap { ll root[2000010]; struct Leftist_Tree { ll ls,rs,val,d; }tree[200010]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) void build_rt(ll rt,ll val) { lson(rt)=rson(rt)=0; tree[rt].val=val; tree[rt].d=1; } ll merge(ll rt1,ll rt2) { if(rt1==0||rt2==0) return rt1+rt2; if(tree[rt1].val<tree[rt2].val) swap(rt1,rt2); rson(rt1)=merge(rson(rt1),rt2); if(tree[lson(rt1)].d<tree[rson(rt1)].d) swap(lson(rt1),rson(rt1)); tree[rt1].d=tree[rson(rt1)].d+1; return rt1; } ll query(ll rt) { return tree[rt].val; } void pop(ll &rt) { rt=merge(lson(rt),rson(rt)); } }T; void dfs(ll x,ll fa) { T.root[x]=x; T.build_rt(x,0); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa) { dfs(e[i].to,x); T.root[x]=T.merge(T.root[x],T.root[e[i].to]); } } T.tree[T.root[x]].val+=a[x]; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,k,u,v,ans=0,i; cin>>n>>k; for(i=1;i<=n;i++) cin>>a[i]; for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } dfs(1,0); for(i=1;i<=k;i++) { ans+=T.query(T.root[1]); T.pop(T.root[1]); } cout<<ans<<endl; return 0; }
P818. 三色卡(card)
P820. 三分图(graph)
1.24
闲话
- 早上起床后发现学校连灯都舍不得给我们开了,在黑灯瞎火中尝试走向机房,跑操自然就取消了。
- 临吃午饭时
说高二的明天下午 点跟着高一的一起放假,为不引起舆情,不致于“树大招风”,他就不在群里发通知了,让我们中午给家里打个电话传达这个消息,明天悄默默地离校就行了。
做题纪要
luogu P6830 [IOI2020] 连接擎天树
luogu P10805 [CEOI2024] 加油站
luogu P4757 [CERC2014] Parades
CF2042D Recommendations
HDU5603 the soldier of love
UOJ 637. 【美团杯2021】A. 数据结构
luogu P5494 【模板】线段树分裂
-
值域有交合并板子。- 正常的
合并只能处理值域无交的情况,因为无法判断第二棵树合并过来的部分具体挂到第一棵树的哪个部分。 - 设当前要合并
两棵平衡树,仍选择 较小的作为根节点。以 为根节点为例,将 中 的部分和 的左儿子合并,剩下的部分和 的右儿子进行合并。- 为保证严格复杂度,实际上应该记录子树内的最大值和最小值决定合并方向。
- 一种比较方便的代码书写形式是类似归并合并的方式,不断将最小值较小的树以另一棵树的最小值为分界分裂成两半,将小的一半合并到答案中,直至一棵为空时将另一棵树合并到答案。
- 为保证严格复杂度,实际上应该记录子树内的最大值和最小值决定合并方向。
- 时间复杂度需要势能分析,详见 "Merging treaps" -- or how to merge sorted sets in good complexity... | 关于一个平衡树相关的问题 。
- 键值
相等的点的需要进行合并情况,否则会影响复杂度。
点击查看记录最大值和最小值代码
ll a[400010]; struct BST { ll root[400010],rt_sum; struct FHQ_Treap { ll son[2],val,minn,maxx,rnd,cnt,siz; }tree[400010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) BST() { rt_sum=0; srand(time(0)); } void pushup(ll rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+tree[rt].cnt; tree[rt].minn=(lson(rt)!=0)?tree[lson(rt)].minn:tree[rt].val; tree[rt].maxx=(rson(rt)!=0)?tree[rson(rt)].maxx:tree[rt].val; } ll build_rt(ll val,ll cnt) { rt_sum++; lson(rt_sum)=rson(rt_sum)=0; tree[rt_sum].val=tree[rt_sum].minn=tree[rt_sum].maxx=val; tree[rt_sum].rnd=rand(); tree[rt_sum].cnt=tree[rt_sum].siz=cnt; return rt_sum; } void split(ll rt,ll val,ll &x,ll &y) { if(rt==0) { x=y=0; return; } if(tree[rt].val<=val) { x=rt; split(rson(rt),val,rson(x),y); pushup(x); } else { y=rt; split(lson(rt),val,x,lson(y)); pushup(y); } } ll merge(ll rt1,ll rt2) { if(rt1==0||rt2==0) return rt1+rt2; if(tree[rt1].rnd<tree[rt2].rnd) { rson(rt1)=merge(rson(rt1),rt2); pushup(rt1); return rt1; } else { lson(rt2)=merge(rt1,lson(rt2)); pushup(rt2); return rt2; } } ll join(ll rt1,ll rt2) { if(rt1==0||rt2==0) return rt1+rt2; if(tree[rt1].maxx<tree[rt2].minn) return merge(rt1,rt2); if(tree[rt2].minn>tree[rt2].maxx) return merge(rt2,rt1); if(tree[rt1].rnd>tree[rt2].rnd) swap(rt1,rt2); ll x,y,z; split(rt2,tree[rt1].val,x,y); split(x,tree[rt1].val-1,x,z); if(z!=0)//合并相同节点 { tree[rt1].cnt+=tree[z].siz; tree[rt1].siz+=tree[z].siz;// z 子树内可能还没有合并,所以要 +siz } lson(rt1)=join(lson(rt1),x); rson(rt1)=join(rson(rt1),y); pushup(rt1); return rt1; } void insert(ll &rt,ll val,ll cnt) { ll x,y,z; split(rt,val,x,y); split(x,val-1,x,z); if(z!=0) { tree[z].cnt+=cnt; tree[z].siz+=cnt; } else z=build_rt(val,cnt); rt=merge(merge(x,z),y); } ll kth_min(ll rt,ll k) { if(rt==0) return -1; if(k<=tree[lson(rt)].siz) return kth_min(lson(rt),k); if(tree[lson(rt)].siz+tree[rt].cnt<k) return kth_min(rson(rt),k-tree[lson(rt)].siz-tree[rt].cnt); return tree[rt].val; } ll query(ll rt,ll l,ll r) { ll x,y,z,ans; split(rt,r,x,y); split(x,l-1,x,z); ans=tree[z].siz; rt=merge(merge(x,z),y); return ans; } ll update(ll &rt,ll l,ll r) { ll x,y,z; split(rt,r,x,y); split(x,l-1,x,z); rt=merge(x,y); return z; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,pd,x,y,z,pos=1,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; T.insert(T.root[1],i,a[i]); } for(i=1;i<=m;i++) { cin>>pd>>x>>y; if(pd==0) { cin>>z; pos++; T.root[pos]=T.update(T.root[x],y,z); } if(pd==1) T.root[x]=T.merge2(T.root[x],T.root[y]); if(pd==2) { cin>>z; T.insert(T.root[x],z,y); } if(pd==3) { cin>>z; cout<<T.query(T.root[x],y,z)<<endl; } if(pd==4) cout<<T.kth_min(T.root[x],y)<<endl; } return 0; }
点击查看归并合并代码
ll a[400010]; struct BST { ll root[400010],rt_sum; struct FHQ_Treap { ll son[2],val,rnd,cnt,siz; }tree[400010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) BST() { rt_sum=0; srand(time(0)); } void pushup(ll rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+tree[rt].cnt; } ll build_rt(ll val,ll cnt) { rt_sum++; lson(rt_sum)=rson(rt_sum)=0; tree[rt_sum].val=val; tree[rt_sum].rnd=rand(); tree[rt_sum].cnt=tree[rt_sum].siz=cnt; return rt_sum; } void split(ll rt,ll val,ll &x,ll &y) { if(rt==0) { x=y=0; return; } if(tree[rt].val<=val) { x=rt; split(rson(rt),val,rson(x),y); pushup(x); } else { y=rt; split(lson(rt),val,x,lson(y)); pushup(y); } } ll merge(ll rt1,ll rt2) { if(rt1==0||rt2==0) return rt1+rt2; if(tree[rt1].rnd<tree[rt2].rnd) { rson(rt1)=merge(rson(rt1),rt2); pushup(rt1); return rt1; } else { lson(rt2)=merge(rt1,lson(rt2)); pushup(rt2); return rt2; } } ll join(ll rt1,ll rt2) { if(rt1==0||rt2==0) return rt1+rt2; ll rt=0; for(;rt2!=0;swap(rt1,rt2))//最小值发生变化,交换 rt1 和 rt2 { ll x=rt2; for(;lson(x)!=0;x=lson(x));//找到最小值 split(rt1,tree[x].val,x,rt1);//这里也应该进行合并 rt=merge(rt,x); } rt=merge(rt,rt1); return rt; } void insert(ll &rt,ll val,ll cnt) { ll x,y; split(rt,val,x,y); rt=merge(merge(x,build_rt(val,cnt)),y); } ll kth_min(ll rt,ll k) { if(rt==0) return -1; if(k<=tree[lson(rt)].siz) return kth_min(lson(rt),k); if(tree[lson(rt)].siz+tree[rt].cnt<k) return kth_min(rson(rt),k-tree[lson(rt)].siz-tree[rt].cnt); return tree[rt].val; } ll query(ll rt,ll l,ll r) { ll x,y,z,ans; split(rt,r,x,y); split(x,l-1,x,z); ans=tree[z].siz; rt=merge(merge(x,z),y); return ans; } ll update(ll &rt,ll l,ll r) { ll x,y,z; split(rt,r,x,y); split(x,l-1,x,z); rt=merge(x,y); return z; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,pd,x,y,z,pos=1,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; T.insert(T.root[1],i,a[i]); } for(i=1;i<=m;i++) { cin>>pd>>x>>y; if(pd==0) { cin>>z; pos++; T.root[pos]=T.update(T.root[x],y,z); } if(pd==1) T.root[x]=T.join(T.root[x],T.root[y]); if(pd==2) { cin>>z; T.insert(T.root[x],z,y); } if(pd==3) { cin>>z; cout<<T.query(T.root[x],y,z)<<endl; } if(pd==4) cout<<T.kth_min(T.root[x],y)<<endl; } return 0; }
- 正常的
luogu P1477 [NOI2008] 假面舞会
1.25
闲话
- 上午
打 accoders NOI 的模拟赛。 - 中午吃饭时从别的奥赛口中得知下午可以把行李拉到教室,到点直接走,遂起床后把行李带到机房了。
- 下午放假前
让收拾了下机房,说机房太乱了。临走的时候从 @Estelle_N 那里顺走了 @yswn 的一个勋章,教练看见后问我们是用的自己头像吗。
做题纪要
1.26
闲话
- 颓。
做题纪要
1.27
闲话
- 颓。
做题纪要
1.28
闲话
- 颓。
做题纪要
1.29
闲话
- 颓。
做题纪要
luogu P11605 [PA 2016] 运算 / Jedynki
-
二进制分解。
点击查看代码
void dfs(int x) { if(x==1) { cout<<"1"; return; } if(x%2==0) cout<<"((1+1)*"; else cout<<"(1+(1+1)*"; dfs(x/2); cout<<")"; } int main() { int t,n,i; cin>>t; for(i=1;i<=t;i++) { cin>>n; dfs(n); cout<<endl; } return 0; }
1.30
闲话
- 颓。
做题纪要
1.31
闲话
- 颓。
做题纪要
[ABC357F] Two Sequence Queries
-
线段树板子。
点击查看代码
const ll p=998244353; ll a[200010],b[200010]; struct SMT { struct SegmemtTree { ll len,ans,sum[2],lazy[2]; }tree[800010]; #define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) void pushup(ll rt) { tree[rt].ans=(tree[lson(rt)].ans+tree[rson(rt)].ans)%p; for(ll i=0;i<=1;i++) tree[rt].sum[i]=(tree[lson(rt)].sum[i]+tree[rson(rt)].sum[i])%p; } void build(ll rt,ll l,ll r) { tree[rt].len=r-l+1; if(l==r) { tree[rt].sum[0]=a[l]; tree[rt].sum[1]=b[l]; tree[rt].ans=a[l]*b[l]%p; return; } ll mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void pushlazy(ll rt,ll lazy,ll op) { tree[rt].ans=(tree[rt].ans+lazy*tree[rt].sum[op^1]%p)%p; tree[rt].sum[op]=(tree[rt].sum[op]+lazy*tree[rt].len%p)%p; tree[rt].lazy[op]=(tree[rt].lazy[op]+lazy)%p; } void pushdown(ll rt) { for(ll i=0;i<=1;i++) { pushlazy(lson(rt),tree[rt].lazy[i],i); pushlazy(rson(rt),tree[rt].lazy[i],i); tree[rt].lazy[i]=0; } } void update(ll rt,ll l,ll r,ll x,ll y,ll val,ll op) { if(x<=l&&r<=y) { pushlazy(rt,val,op); return; } pushdown(rt); ll mid=(l+r)/2; if(x<=mid) update(lson(rt),l,mid,x,y,val,op); if(y>mid) update(rson(rt),mid+1,r,x,y,val,op); pushup(rt); } ll query(ll rt,ll l,ll r,ll x,ll y) { if(x<=l&&r<=y) return tree[rt].ans; pushdown(rt); ll mid=(l+r)/2; if(y<=mid) return query(lson(rt),l,mid,x,y); if(x>mid) return query(rson(rt),mid+1,r,x,y); return (query(lson(rt),l,mid,x,y)+query(rson(rt),mid+1,r,x,y))%p; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,pd,l,r,x,i; cin>>n>>m; for(i=1;i<=n;i++) cin>>a[i]; for(i=1;i<=n;i++) cin>>b[i]; T.build(1,1,n); for(i=1;i<=m;i++) { cin>>pd>>l>>r; if(pd!=3) { cin>>x; T.update(1,1,n,l,r,x,pd-1); } else cout<<T.query(1,1,n,l,r)<<endl; } return 0; }
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18684419,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下