1.数据结构

数据结构1

开题顺序: \(BXGEWDJVNCFQKPLAHR\)

\(A\) luogu P5494 【模板】线段树分裂

\(B\) luogu P1637 三元上升子序列

\(C\) luogu P6492 [COCI2010-2011#6] STEP

  • L 视作 \(0\)R 视作 \(1\)

  • 则等价于查询最长 \(01\) 串长度,线段树维护前后缀信息即可,写法跟维护最大子段和差不多。

    点击查看代码
    int a[200010];
    struct SMT
    {
    	struct SegmentTree
    	{
    		int l,r,pre,suf,ans;
    	}tree[800010];
    	int lson(int x)
    	{
    		return x*2;
    	}
    	int rson(int x)
    	{
    		return x*2+1;
    	}
    	void pushup(int rt)
    	{
    		tree[rt].ans=max(tree[lson(rt)].ans,tree[rson(rt)].ans);
    		tree[rt].pre=tree[lson(rt)].pre;
    		tree[rt].suf=tree[rson(rt)].suf;
    		if(a[tree[lson(rt)].r]!=a[tree[rson(rt)].l])
    		{
    			tree[rt].ans=max(tree[rt].ans,tree[lson(rt)].suf+tree[rson(rt)].pre);
    			tree[rt].pre+=(tree[lson(rt)].pre==tree[lson(rt)].r-tree[lson(rt)].l+1)*tree[rson(rt)].pre;
    			tree[rt].suf+=(tree[rson(rt)].suf==tree[rson(rt)].r-tree[rson(rt)].l+1)*tree[lson(rt)].suf;
    		}
    	}
    	void build(int rt,int l,int r)
    	{
    		tree[rt].l=l;
    		tree[rt].r=r;
    		if(l==r)
    		{
    			tree[rt].pre=tree[rt].suf=tree[rt].ans=1;
    			return;
    		}
    		int mid=(l+r)/2;
    		build(lson(rt),l,mid);
    		build(rson(rt),mid+1,r);
    		pushup(rt);
    	}
    	void update(int rt,int pos)
    	{
    		if(tree[rt].l==tree[rt].r)
    		{
    			a[tree[rt].l]^=1;
    			return;
    		}
    		int mid=(tree[rt].l+tree[rt].r)/2;
    		if(pos<=mid)
    		{
    			update(lson(rt),pos);
    		}
    		else
    		{
    			update(rson(rt),pos);		
    		}
    		pushup(rt);
    	}
    	SegmentTree query(int rt,int x,int y)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)
    		{
    			return tree[rt];
    		}
    		int mid=(tree[rt].l+tree[rt].r)/2;
    		SegmentTree p,q,ans;
    		if(y<=mid)
    		{
    			return query(lson(rt),x,y);
    		}
    		else
    		{
    			if(x>mid)
    			{
    				return query(rson(rt),x,y);
    			}
    			else
    			{
    				p=query(lson(rt),x,y);
    				q=query(rson(rt),x,y);
    				ans.ans=max(p.ans,q.ans);
    				ans.pre=p.pre;
    				ans.suf=q.suf;
    				if(a[p.r]!=a[q.l])
    				{
    					ans.ans=max(ans.ans,p.suf+q.pre);
    					ans.pre+=(p.pre==p.r-p.l+1)*q.pre;
    					ans.suf+=(q.suf==q.r-q.l+1)*p.suf;
    				}
    				return ans;
    			}
    		}
    	}
    }T;
    int main()
    {
    	int n,m,x,i;
    	cin>>n>>m;
    	T.build(1,1,n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>x;
    		T.update(1,x);
    		cout<<T.query(1,1,n).ans<<endl;
    	}
    	return 0;
    }
    

\(D\) luogu P6136 【模板】普通平衡树(数据加强版)

\(E\) luogu P3038 [USACO11DEC] Grass Planting G

\(F\) luogu P7735 [NOI2021] 轻重边

  • 维护路径信息,考虑树剖。

  • 边权直接维护不太好维护,考虑转成点权。而直接将边权信息赋给深度较深的儿子节点在本题中难以适应。

  • 考虑给每一个点赋一个权值 \(val\) 使得对于任意一条边 \(u \to v\)\(val_{u}=val_{v}\)\(u \to v\) 是重边,否则是轻边。

  • 此时操作 \(1\) 等价于将 \(u \to v\) 上的点的权值都赋成一个统一的值;操作 \(2\) 等价于查询 \(u \to v\) 上相邻两点权值相同的无序点对,做法同 luogu P2486 [SDOI2011] 染色 差不多,线段树维护即可。

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[200010];
    int head[200010],c[200010],cc[200010],fa[200010],dep[200010],siz[200010],son[200010],dfn[200010],top[200010],cnt=0,tot=0;
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    struct SMT
    {
    	struct SegmentTree
    	{
    		int l,r,lazy,lc,rc,ans;
    	}tree[800010];
    	int lson(int x)
    	{
    		return x*2;
    	}
    	int rson(int x)
    	{
    		return x*2+1;
    	}
    	void pushup(int rt)
    	{
    		tree[rt].ans=tree[lson(rt)].ans+tree[rson(rt)].ans+(tree[lson(rt)].rc==tree[rson(rt)].lc);
    		tree[rt].lc=tree[lson(rt)].lc;
    		tree[rt].rc=tree[rson(rt)].rc;
    	}
    	void build(int rt,int l,int r)
    	{
    		tree[rt].l=l;
    		tree[rt].r=r;
    		tree[rt].lazy=0;
    		if(l==r)
    		{
    			tree[rt].lc=tree[rt].rc=cc[l];
    			tree[rt].ans=0;
    			return;
    		}
    		int mid=(l+r)/2;
    		build(lson(rt),l,mid);
    		build(rson(rt),mid+1,r);
    		pushup(rt);
    	}
    	void pushdown(int rt)
    	{
    		if(tree[rt].lazy!=0)
    		{
    			tree[lson(rt)].lc=tree[lson(rt)].rc=tree[rt].lazy;
    			tree[rson(rt)].lc=tree[rson(rt)].rc=tree[rt].lazy;
    			tree[lson(rt)].ans=tree[lson(rt)].r-tree[lson(rt)].l+1-1;
    			tree[rson(rt)].ans=tree[rson(rt)].r-tree[rson(rt)].l+1-1;
    			tree[lson(rt)].lazy=tree[rson(rt)].lazy=tree[rt].lazy;
    			tree[rt].lazy=0;
    		}
    	}
    	void update(int rt,int x,int y,int val)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)	
    		{
    			tree[rt].lc=tree[rt].rc=tree[rt].lazy=val;
    			tree[rt].ans=tree[rt].r-tree[rt].l+1-1;
    			return;
    		}
    		pushdown(rt);
    		int mid=(tree[rt].l+tree[rt].r)/2;
    		if(x<=mid)
    		{
    			update(lson(rt),x,y,val);
    		}
    		if(y>mid)
    		{
    			update(rson(rt),x,y,val);
    		}
    		pushup(rt);
    	}
    	SegmentTree query(int rt,int x,int y)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)
    		{
    			return tree[rt];
    		}
    		pushdown(rt);
    		int mid=(tree[rt].l+tree[rt].r)/2;
    		SegmentTree p,q,ans;
    		if(y<=mid)
    		{
    			return query(lson(rt),x,y);
    		}
    		else
    		{
    			if(x>mid)
    			{
    				return query(rson(rt),x,y);
    			}
    			else
    			{
    				p=query(lson(rt),x,y);
    				q=query(rson(rt),x,y);
    				ans.ans=p.ans+q.ans+(p.rc==q.lc);
    				ans.lc=p.lc;
    				ans.rc=q.rc;
    				return ans;
    			}
    		}
    	}
    }T;
    void dfs1(int x,int father)
    {
    	siz[x]=1;
    	fa[x]=father;
    	dep[x]=dep[father]+1;
    	for(int 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(int x,int id)
    {
    	top[x]=id;
    	tot++;
    	dfn[x]=tot;
    	cc[tot]=c[x];
    	if(son[x]!=0)
    	{
    		dfs2(son[x],id);
    		for(int 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);
    			}
    		}
    	}
    }
    void update1(int u,int v,int val)
    {
    	while(top[u]!=top[v])
    	{
    		if(dep[top[u]]>dep[top[v]])
    		{
    			T.update(1,dfn[top[u]],dfn[u],val);
    			u=fa[top[u]];
    		}
    		else
    		{
    			T.update(1,dfn[top[v]],dfn[v],val);
    			v=fa[top[v]];
    		}
    	}
    	if(dep[u]<dep[v])
    	{
    		T.update(1,dfn[u],dfn[v],val);
    	}
    	else
    	{
    		T.update(1,dfn[v],dfn[u],val);
    	}
    }
    int query1(int u,int v)
    {
    	int ans=0;
    	while(top[u]!=top[v])
    	{
    		if(dep[top[u]]>dep[top[v]])
    		{
    			ans+=T.query(1,dfn[top[u]],dfn[u]).ans+(T.query(1,dfn[top[u]],dfn[top[u]]).lc==T.query(1,dfn[fa[top[u]]],dfn[fa[top[u]]]).lc);
    			u=fa[top[u]];
    		}
    		else
    		{
    			ans+=T.query(1,dfn[top[v]],dfn[v]).ans+(T.query(1,dfn[top[v]],dfn[top[v]]).lc==T.query(1,dfn[fa[top[v]]],dfn[fa[top[v]]]).lc);
    			v=fa[top[v]];
    		}
    	}
    	if(dep[u]<dep[v])
    	{
    		ans+=T.query(1,dfn[u],dfn[v]).ans;
    	}
    	else
    	{
    		ans+=T.query(1,dfn[v],dfn[u]).ans;
    	}
    	return ans;
    }
    int main()
    {
    	int t,n,m,pd,u,v,i,j;
    	scanf("%d",&t);
    	for(j=1;j<=t;j++)
    	{
    		cnt=tot=0;
    		memset(e,0,sizeof(e));
    		memset(head,0,sizeof(head));
    		memset(son,0,sizeof(son));
    		scanf("%d%d",&n,&m);
    		for(i=1;i<=n;i++)
    		{
    			c[i]=i;
    		}
    		for(i=1;i<=n-1;i++)
    		{
    			scanf("%d%d",&u,&v);
    			add(u,v);
    			add(v,u);
    		}
    		dfs1(1,0);
    		dfs2(1,1);
    		T.build(1,1,n);
    		for(i=1;i<=m;i++)
    		{
    			scanf("%d%d%d",&pd,&u,&v);
    			if(pd==1)
    			{
    				update1(u,v,n+i);
    			}
    			else
    			{
    				printf("%d\n",query1(u,v));
    			}
    		}
    	}
    	return 0;
    }
    

\(G\) luogu P3605 [USACO17JAN] Promotion Counting P

\(H\) luogu P7394 「TOCO Round 1」History

  • \(x\) 深度相等且节点距离为 \(y\) 的节点一定是 \(x\)\(\frac{y}{2}\) 级祖先的 \(\frac{y}{2}\) 的子孙。

    • 同时也解释了当 \(y\) 为奇数时答案为 \(0\)
  • 考虑对于每个深度开一棵动态开点线段树,查询在 \(x\)\(\frac{y}{2}\) 级祖先的子树且不在 \(x\)\(\frac{y}{2}-1\) 级祖先的子树中的开着灯的节点数量, \(DFS\) 序维护即可。

    • 因为直接查询 \(x\)\(\frac{y}{2}\) 级祖先的子树中深度和 \(x\) 的深度相等的情况下,会将 \(x\)\(\frac{y}{2}-1\) 级祖先的 \(\frac{y}{2}-1\) 级子孙也统计到,需要减去这部分贡献。
  • 对于回溯操作,操作树或主席树维护即可。其中后者的空间复杂度多带一个 \(2\) 倍常数。

    点击查看代码
    struct node
    {
        int nxt,to;
    }e[200010];
    struct ask
    {
        int pd,x,y,id;
    }q[200010];
    int head[200010],a[200010],dep[200010],dfn[200010],out[200010],fa[200010][20],ans[200010],cnt=0,tot=0,N;
    vector<int>E[200010];
    void add(int u,int v)
    {
        cnt++;
        e[cnt].nxt=head[u];
        e[cnt].to=v;
        head[u]=cnt;
    }
    void dfs1(int x,int father)
    {
        tot++;
        dfn[x]=tot;
        fa[x][0]=father;
        dep[x]=dep[father]+1;
        for(int i=1;(1<<i)<=dep[x];i++)
        {
            fa[x][i]=fa[fa[x][i-1]][i-1];
        }
        for(int i=head[x];i!=0;i=e[i].nxt)
        {
            if(e[i].to!=father)
            {
                dfs1(e[i].to,x);
            }
        }
        out[x]=tot;
    }
    int kth_fa(int x,int k)
    {
        int rt=x;
        for(int i=N;i>=0;i--)
        {
            if(dep[x]-dep[fa[rt][i]]<=k)
            {
                rt=fa[rt][i];
            }
        }
        return rt;
    }
    struct SMT
    {
        int root[200010],rt_sum;
        struct SegmentTree
        {
            int ls,rs,sum;
        }tree[200010<<5];
        #define lson(rt) (tree[rt].ls)
        #define rson(rt) (tree[rt].rs)
        int build_rt()
        {
            rt_sum++;
            lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0;
            return rt_sum;
        }
        void pushup(int rt)
        {
            tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum;
        }
        void update(int &rt,int l,int r,int pos,int val)
        {
            rt=(rt==0)?build_rt():rt;
            if(l==r)
            {
                tree[rt].sum=val;
                return;
            }
            int mid=(l+r)/2;
            if(pos<=mid)
            {
                update(lson(rt),l,mid,pos,val);
            }
            else
            {
                update(rson(rt),mid+1,r,pos,val);
            }
            pushup(rt);
        }
        int query(int rt,int l,int r,int x,int y)
        {
            if(rt==0)
            {
                return 0;
            }
            if(x<=l&&r<=y)
            {
                return tree[rt].sum;
            }
            int mid=(l+r)/2,ans=0;
            if(x<=mid)
            {
                ans+=query(lson(rt),l,mid,x,y);
            }
            if(y>mid)
            {
                ans+=query(rson(rt),mid+1,r,x,y);
            }
            return ans;
        }
    }T;
    void dfs2(int x,int n)
    {
        if(q[x].pd==1)
        {
            a[q[x].x]^=1;
            T.update(T.root[dep[q[x].x]],1,n,dfn[q[x].x],a[q[x].x]);
        }
        if(q[x].pd==2)
        {
            if(q[x].y%2==0)
            {
                if(q[x].y==0)
                {
                    ans[q[x].id]=a[q[x].x];
                }
                else
                {
                    int rt1=kth_fa(q[x].x,q[x].y/2),rt2=kth_fa(q[x].x,q[x].y/2-1);
                    ans[q[x].id]=T.query(T.root[dep[q[x].x]],1,n,dfn[rt1],out[rt1])-T.query(T.root[dep[q[x].x]],1,n,dfn[rt2],out[rt2]);
                }
            }
        }
        for(int i=0;i<E[x].size();i++)
        {
            dfs2(E[x][i],n);
        }
        if(q[x].pd==1)
        {
            a[q[x].x]^=1;
            T.update(T.root[dep[q[x].x]],1,n,dfn[q[x].x],a[q[x].x]);
        }
    }
    int main()
    {
        int n,m,u,v,i;
        cin>>n;
        N=log2(n)+1;
        for(i=1;i<=n-1;i++)
        {
            cin>>u>>v;
            add(u,v);
            add(v,u);
        }
        cin>>m;
        for(i=1;i<=m;i++)
        {
            cin>>q[i].pd>>q[i].x;
            q[i].id=i;
            if(q[i].pd==2)
            {
                cin>>q[i].y;
            }
            if(q[i].pd<=2)
            {
                E[i-1].push_back(i);
            }
            else
            {
                E[q[i].x].push_back(i);
            }
        }
        dfs1(1,0);
        dfs2(0,n);
        for(i=1;i<=m;i++)
        {
            if(q[i].pd==2)
            {
                cout<<ans[i]<<endl;
            }
        }
        return 0;
    }
    

\(I\) luogu P7671 [GDOI2016] 疯狂动物城

\(J\) CF915E Physical Education Lessons

\(K\) CF19D Points

  • 离散化后线段树套 set 即可。

  • 叶子节点只保留 \(y\) 坐标的最大值然后进行线段树上二分。

    点击查看代码
    int x[200010],y[200010],xx[200010],yy[200010];
    char pd[200010][8];
    set<int>s[200010];
    struct SMT
    {
    	struct SegmentTree
    	{
    		int l,r,maxx;
    	}tree[800010];
    	int lson(int x)
    	{
    		return x*2;
    	}
    	int rson(int x)
    	{
    		return x*2+1;
    	}
    	void pushup(int rt)
    	{
    		tree[rt].maxx=max(tree[lson(rt)].maxx,tree[rson(rt)].maxx);
    	}
    	void build(int rt,int l,int r)
    	{
    		tree[rt].l=l;
    		tree[rt].r=r;
    		if(l==r)
    		{
    			s[l].insert(-0x7f7f7f7f);
    			tree[rt].maxx=-0x7f7f7f7f;
    			return;
    		}
    		int mid=(l+r)/2;
    		build(lson(rt),l,mid);
    		build(rson(rt),mid+1,r);
    		pushup(rt);
    	}
    	void update(int rt,int pos,int val)
    	{
    		if(tree[rt].l==tree[rt].r)
    		{
    			tree[rt].maxx=val;
    			return;
    		}
    		int mid=(tree[rt].l+tree[rt].r)/2;
    		if(pos<=mid)
    		{
    			update(lson(rt),pos,val);
    		}
    		else
    		{
    			update(rson(rt),pos,val);
    		}
    		pushup(rt);
    	}
    	int query(int rt,int x,int y,int val)
    	{
    		if(tree[rt].maxx<val)
    		{
    			return -1;
    		}
    		if(tree[rt].l==tree[rt].r)
    		{
    			return tree[rt].l;
    		}
    		int mid=(tree[rt].l+tree[rt].r)/2,p,q;
    		if(y<=mid)
    		{
    			return query(lson(rt),x,y,val);
    		}
    		else
    		{
    			if(x>mid)
    			{
    				return query(rson(rt),x,y,val);
    			}
    			else
    			{
    				p=query(lson(rt),x,y,val);
    				if(p!=-1)
    				{
    					return p;
    				}
    				q=query(rson(rt),x,y,val);
    				if(q!=-1)
    				{
    					return q;
    				}
    				return -1;
    			}
    		}
    	}
    }T;
    int main()
    {
    	int n,ans,i;
    	scanf("%d",&n);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%s%d%d",(pd[i]+1),&x[i],&y[i]);
    		xx[i]=x[i];
    		yy[i]=y[i];
    	}
    	sort(xx+1,xx+1+n);
    	sort(yy+1,yy+1+n);
    	xx[0]=unique(xx+1,xx+1+n)-(xx+1);
    	yy[0]=unique(yy+1,yy+1+n)-(yy+1);
    	T.build(1,1,xx[0]);
    	for(i=1;i<=n;i++)
    	{
    		x[i]=lower_bound(xx+1,xx+1+xx[0],x[i])-xx;
    		y[i]=lower_bound(yy+1,yy+1+yy[0],y[i])-yy;
    		if(pd[i][1]=='a')
    		{
    			ans=*--s[x[i]].end();
    			s[x[i]].insert(y[i]);
    			if(ans!=*--s[x[i]].end())
    			{
    				T.update(1,x[i],*--s[x[i]].end());
    			}
    		}
    		if(pd[i][1]=='r')
    		{
    			ans=*--s[x[i]].end();
    			s[x[i]].erase(s[x[i]].find(y[i]));
    			if(ans!=*--s[x[i]].end())
    			{
    				T.update(1,x[i],*--s[x[i]].end());
    			}
    		}
    		if(pd[i][1]=='f')
    		{
    			if(x[i]+1<=xx[0])
    			{
    				ans=T.query(1,x[i]+1,xx[0],y[i]+1);
    				if(ans==-1)
    				{
    					printf("-1\n");
    				}
    				else
    				{
    					printf("%d %d\n",xx[ans],yy[*s[ans].upper_bound(y[i])]);
    				}
    			}
    			else
    			{
    				printf("-1\n");
    			}
    		}
    	}
    	return 0;
    }
    

\(L\) CF193D Two Segments

  • 翻译过来就是询问 \(1 \sim n\) 中有多少个值域连续段 \([l,r]\) 满足 \(\{ a \}\) 中值在 \([l,r]\) 之间的数所形成的区间个数 \(\le 2\)

  • \(\forall i \in [1,n],pos_{a_{i}}=i\)

  • 尝试枚举值域连续段的右端点 \(r\) ,每次统计 \([1,r]\) 中有多少个合法的左端点 \(l \in [1,r]\) 使得 \([l,r]\) 能被分成 \(1\) 段或 \(2\) 段。而这个过程可以统计 \([1,r]\) 至少、次少分成的区间个数(最后判断一下是否 \(\le 2\) )及各自方案数进行统计。

  • 具体地,设 \(f_{l,r}\) 表示值在 \([l,r]\) 中的数组成区间的个数, \(S_{l,r}\) 表示值在 \([l,r]\) 中的数的下标组成的集合。

  • 考虑右端点由 \(r-1\) 变成 \(r\)\(f\) 的影响。首先不难有 \(\forall l \in [1,r],pos_{r} \in S_{l,r}\) ,故假设 \(r\) 会单独成一段,即 \(f_{l,r}+=1(l \in [1,r])\) ;接着若 \(a_{pos_{r}-1}<r\)\(\forall l \in [1,a_{pos_{r}-1}],pos_{r} \in S_{l,r} \land pos_{r}-1 \in S_{l,r}\) ,故 \(pos_{r}\) 可以和 \(pos_{r}-1\) 拼起来,使区间个数少一,即 \(f_{l,r}-=1(l \in [1,a_{pos_{r}-1}])\) ;最后若 \(a_{pos_{r}+1}<r\)\(\forall l \in [1,a_{pos_{r}+1}],pos_{r} \in S_{l,r} \land pos_{r}+1 \in S_{l,r}\) ,故 \(pos_{r}\) 可以和 \(pos_{r}+1\) 拼起来,使区间个数少一,即 \(f_{l,r}-=1(l \in [1,a_{pos_{r}+1}])\)

  • 线段树维护区间修改、区间查询即可。

    点击查看代码
    ll a[300010],pos[300010];
    struct SMT
    {
    	pair<ll,ll>tmp[6];
    	struct SegmentTree
    	{
    		ll l,r,lazy;
    		pair<ll,ll>zx,cx;
    	}tree[1200010];
    	ll lson(ll x)
    	{
    		return x*2;
    	}
    	ll rson(ll x)
    	{
    		return x*2+1;
    	}
    	void pushup(ll rt)
    	{
    		tmp[1]=tree[lson(rt)].zx;
    		tmp[2]=tree[lson(rt)].cx;
    		tmp[3]=tree[rson(rt)].zx;
    		tmp[4]=tree[rson(rt)].cx;
    		tmp[5]=make_pair(0x3f3f3f3f,0);
    		sort(tmp+1,tmp+5);
    		tree[rt].zx=tree[rt].cx=make_pair(0,0);
    		ll pos=1;
    		do 
    		{
    			tree[rt].zx.first=tmp[pos].first;
    			tree[rt].zx.second+=tmp[pos].second;
    			pos++;
    		}while(pos<=4&&tmp[pos].first==tree[rt].zx.first);
    		do
    		{
    			tree[rt].cx.first=tmp[pos].first;
    			tree[rt].cx.second+=tmp[pos].second;
    			pos++;
    		}while(pos<=4&&tmp[pos].first==tree[rt].cx.first);
    	}
    	void build(ll rt,ll l,ll r)
    	{
    		tree[rt].l=l;
    		tree[rt].r=r;
    		if(l==r)
    		{
    			tree[rt].zx=make_pair(0,1);
    			tree[rt].cx=make_pair(0x3f3f3f3f,0);
    			return;
    		}
    		ll mid=(l+r)/2;
    		build(lson(rt),l,mid);
    		build(rson(rt),mid+1,r);
    		pushup(rt);
    	}
    	void pushdown(ll rt)
    	{
    		if(tree[rt].lazy!=0)
    		{
    			tree[lson(rt)].zx.first+=tree[rt].lazy;
    			tree[lson(rt)].cx.first+=tree[rt].lazy;
    			tree[rson(rt)].zx.first+=tree[rt].lazy;
    			tree[rson(rt)].cx.first+=tree[rt].lazy;
    			tree[lson(rt)].lazy+=tree[rt].lazy;
    			tree[rson(rt)].lazy+=tree[rt].lazy;
    			tree[rt].lazy=0;
    		}
    	}
    	void update(ll rt,ll x,ll y,ll val)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)
    		{
    			tree[rt].zx.first+=val;
    			tree[rt].cx.first+=val;
    			tree[rt].lazy+=val;
    			return;
    		}
    		pushdown(rt);
    		ll mid=(tree[rt].l+tree[rt].r)/2;
    		if(x<=mid)
    		{
    			update(lson(rt),x,y,val);
    		}
    		if(y>mid)
    		{
    			update(rson(rt),x,y,val);
    		}
    		pushup(rt);
    	}
    	ll query(ll rt,ll x,ll y)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)
    		{
    			return tree[rt].zx.second*(tree[rt].zx.first<=2)+tree[rt].cx.second*(tree[rt].cx.first<=2);
    		}
    		pushdown(rt);
    		ll mid=(tree[rt].l+tree[rt].r)/2,ans=0;
    		if(x<=mid)//对子树分治时在此题中不需要再进行 pushup 因为最小值、次小值可以直接合并
    		{
    			ans+=query(lson(rt),x,y);
    		}
    		if(y>mid)
    		{
    			ans+=query(rson(rt),x,y);
    		}
    		return ans;
    	}
    }T;
    int main()
    {
    	ll n,ans=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		pos[a[i]]=i;
    	}
    	T.build(1,1,n);
    	for(i=1;i<=n;i++)
    	{
    		T.update(1,1,i,1);
    		if(pos[i]-1>=1&&a[pos[i]-1]<i)
    		{
    			T.update(1,1,a[pos[i]-1],-1);
    		}
    		if(pos[i]+1<=n&&a[pos[i]+1]<i)
    		{
    			T.update(1,1,a[pos[i]+1],-1);
    		}
    		if(i-1>=1)
    		{
    			ans+=T.query(1,1,i-1);
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

\(M\) CF1983F array-value

\(N\) [ABC371F] Takahashi in Narrow Road

\(O\) CF1304F2 Animal Observation (hard version)

\(P\) CF946G Almost Increasing Array

  • 若将 \(\{ a \}\) 变成严格递增序列,至少需要更改 \(n\) 减去 \(\{ a_{i}-i \}\) 的最长不下降子序列长度个数。

    • 证明
      • 对于 \(a_{i},a_{j}(i<j)\) 若都在最终的严格递增序列里,则有 \(a_{i}-a_{j} \le i-j\) ,即 \(a_{i}-i \le a_{j}-j\) 。而这 \(\{ a_{i}-i \}\) 的最长不下降子序列长度个数是不需要更改的。
  • 考虑计算最多能保留的数的个数。

  • \(\forall i \in [1,n],b_{i}=a_{i}-i\)

  • \(f_{i,0/1}\) 表示以 \(i\) 结尾的前缀中没有/有删除过的数时(删除的这个数仅能 \(\in [1,i-1]\) ,能够在最终保留但不参与运算)最多能保留的数的个数,状态转移方程为 \(\begin{cases} f_{i,0}=\max\limits_{j=1}^{i-1} \{ [b_{j} \le b_{i}] \times (f_{j,0}+1) \} \\ f_{i,1}=\max(\max\limits_{j=1}^{i-1} \{ [b_{j} \le b_{i}] \times (f_{j,1}+1) \},\max\limits_{j=1}^{i-2} \{ [b_{j} \le b_{i}+1] \times (f_{j,0}+1) \}) \end{cases}\) ,边界为 \(\begin{cases} f_{1,0}=1 \\ f_{1,1}=0 \end{cases}\)

  • 权值树状数组优化 \(DP\) 即可。

  • 最终,有 \(\max(n-1-\max\limits_{i=1}^{n} \{ f_{i,0},f_{i,1} \},0)\) 即为所求。

    • 使用删除一定比不使用删除不劣。
    点击查看代码
    ll a[200010],b[200010],c[400010],f[200010][2];
    struct BIT
    {
    	ll c[400010];
    	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]=max(c[i],val);
    		}
    	}
    	ll getsum(ll x)
    	{
    		ll ans=0;
    		for(ll i=x;i>=1;i-=lowbit(i))
    		{
    			ans=max(ans,c[i]);
    		}
    		return ans;
    	}
    }T[3];
    int main()
    {
    	ll n,ans=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		b[i]=a[i]-i;
    		c[2*i-1]=b[i];
    		c[2*i]=b[i]+1;
    	}
    	sort(c+1,c+2*n+1);
    	c[0]=unique(c+1,c+2*n+1)-(c+1);
    	for(i=1;i<=n;i++)
    	{
    		f[i][0]=T[0].getsum(lower_bound(c+1,c+1+c[0],b[i])-c)+1;
    		f[i][1]=T[1].getsum(lower_bound(c+1,c+1+c[0],b[i])-c);
    		f[i][1]+=(f[i][1]!=0);
    		if(i-2>=1)
    		{
    			f[i][1]=max(f[i][1],T[2].getsum(lower_bound(c+1,c+1+c[0],b[i]+1)-c)+1);		
    		}
    		T[0].add(c[0],lower_bound(c+1,c+1+c[0],b[i])-c,f[i][0]);
    		T[1].add(c[0],lower_bound(c+1,c+1+c[0],b[i])-c,f[i][1]);
    		if(i-1>=1)
    		{
    			T[2].add(c[0],lower_bound(c+1,c+1+c[0],b[i-1])-c,f[i-1][0]);
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		ans=max(ans,max(f[i][0],f[i][1]));
    	}
    	cout<<max(n-1-ans,0ll)<<endl;
    	return 0;
    }
    

\(Q\) CF718C Sasha and Array

  • 容易有 \(\begin{bmatrix} Fib_{n} & Fib_{n+1} \end{bmatrix}=\begin{bmatrix} Fib_{n-1} & Fib_{n} \end{bmatrix} \times \begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}\)

  • 建树时暴力算斐波那契数,修改时维护 \(\begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}\) 的乘方标记,区间和维护矩阵加法即可。

    • 本质上应用了矩阵的结合律 \(A \times B+A \times C=A \times (B+C)\)
  • 为方便重载运算符,将原 \(F\) 矩阵写作 \(\begin{bmatrix} Fib_{n} & Fib_{n+1} \\ 0 & 0 \end{bmatrix}\)

    点击查看代码
    const ll p=1000000007;
    struct Matrix
    {
    	ll ma[3][3];
    	Matrix()
    	{
    		memset(ma,0,sizeof(ma));
    	}
    	Matrix operator + (const Matrix &another) const
    	{
    		Matrix ans;
    		for(ll i=1;i<=2;i++)
    		{
    			for(ll j=1;j<=2;j++)
    			{
    				ans.ma[i][j]=(ma[i][j]+another.ma[i][j])%p;
    			}
    		}
    		return ans;
    	}
    	Matrix operator * (const Matrix &another) const
    	{
    		Matrix ans;
    		for(ll i=1;i<=2;i++)
    		{
    			for(ll j=1;j<=2;j++)
    			{
    				for(ll h=1;h<=2;h++)
    				{
    					ans.ma[i][j]=(ans.ma[i][j]+ma[i][h]*another.ma[h][j]%p)%p;
    				}
    			}
    		}
    		return ans;
    	}
    	bool operator == (const Matrix &another) const
    	{
    		for(ll i=1;i<=2;i++)
    		{
    			for(ll j=1;j<=2;j++)
    			{
    				if(ma[i][j]!=another.ma[i][j])
    				{
    					return false;
    				}
    			}
    		}
    		return true;
    	}
    }base,I;
    ll a[100010];
    Matrix qpow(Matrix a,ll b,ll p)
    {
    	Matrix ans;
    	for(ll i=1;i<=2;i++)
    	{
    		ans.ma[i][i]=1;
    	}
    	while(b)
    	{
    		if(b&1)
    		{
    			ans=ans*a;
    		}
    		b>>=1;
    		a=a*a;
    	}
    	return ans;
    }
    struct SMT
    {
    	struct SegmentTree
    	{
    		ll l,r;
    		Matrix sum,lazy;
    	}tree[400010];
    	ll lson(ll x)
    	{
    		return x*2;
    	}
    	ll rson(ll x)
    	{
    		return x*2+1;
    	}
    	void pushup(ll rt)
    	{
    		tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum;
    	}
    	void build(ll rt,ll l,ll r)
    	{
    		tree[rt].l=l;
    		tree[rt].r=r;
    		tree[rt].lazy=I;
    		if(l==r)
    		{
    			tree[rt].sum.ma[1][1]=tree[rt].sum.ma[2][1]=tree[rt].sum.ma[2][2]=0;
    			tree[rt].sum.ma[1][2]=1;
    			tree[rt].sum=tree[rt].sum*qpow(base,a[l],p);
    			return;
    		}
    		ll mid=(l+r)/2;
    		build(lson(rt),l,mid);
    		build(rson(rt),mid+1,r);
    		pushup(rt);
    	}
    	void pushdown(ll rt)
    	{
    		if(!(tree[rt].lazy==I))
    		{
    			tree[lson(rt)].sum=tree[lson(rt)].sum*tree[rt].lazy;
    			tree[rson(rt)].sum=tree[rson(rt)].sum*tree[rt].lazy;
    			tree[lson(rt)].lazy=tree[lson(rt)].lazy*tree[rt].lazy;
    			tree[rson(rt)].lazy=tree[rson(rt)].lazy*tree[rt].lazy;
    			tree[rt].lazy=I;
    		}
    	}
    	void update(ll rt,ll x,ll y,Matrix val)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)
    		{
    			tree[rt].sum=tree[rt].sum*val;
    			tree[rt].lazy=tree[rt].lazy*val;
    			return;
    		}
    		pushdown(rt);
    		ll mid=(tree[rt].l+tree[rt].r)/2;
    		if(x<=mid)
    		{
    			update(lson(rt),x,y,val);
    		}
    		if(y>mid)
    		{
    			update(rson(rt),x,y,val);
    		}
    		pushup(rt);
    	}
    	ll query(ll rt,ll x,ll y)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)
    		{
    			return tree[rt].sum.ma[1][1];
    		}
    		pushdown(rt);
    		int mid=(tree[rt].l+tree[rt].r)/2,ans=0;
    		if(x<=mid)
    		{
    			ans=(ans+query(lson(rt),x,y))%p;
    		}
    		if(y>mid)
    		{
    			ans=(ans+query(rson(rt),x,y))%p;
    		}
    		return ans;
    	}
    }T;
    int main()
    {
    	ll n,m,pd,l,r,x,i;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	base.ma[1][1]=0;
    	base.ma[1][2]=base.ma[2][1]=base.ma[2][2]=1;
    	I.ma[1][1]=I.ma[2][2]=1;
    	I.ma[1][2]=I.ma[2][1]=0;
    	T.build(1,1,n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd>>l>>r;
    		if(pd==1)
    		{
    			cin>>x;
    			T.update(1,l,r,qpow(base,x,p));
    		}
    		else
    		{
    			cout<<T.query(1,l,r)<<endl;
    		}
    	}
    	return 0;
    }
    

\(R\) CF1956F Nene and the Passing Game

  • 等价于求建完图后的连通块个数。
  • \((i,j) \in E\) 当且仅当 \(|i-j| \in [l_{i}+l_{j},r_{i}+r_{j}]\) ,即 \(\begin{cases} i+l_{i} \le j-l_{j} \land i+r_{i} \ge j-r_{j} & i<j \\ j+l_{j} \le i-l_{i} \land j+r_{j} \ge i-r_{i} & i>j \end{cases}\) ,即 \(\begin{cases} [i+l_{i},i+r_{i}] \bigcap [j-l_{j},j-r_{j}] \ne \varnothing & i<j \\ [j+l_{j},j+r_{j}] \bigcap [i-l_{i},i-r_{i}] \ne \varnothing & i>j \end{cases}\)
  • \(\forall i \in [1,n],L_{i}=[i-l_{i},i-r_{i}],R_{i}=[i+l_{i},i+r_{i}]\) ,上述条件转化为 \(\begin{cases} R_{i} \bigcap L_{j} \ne \varnothing & i<j \\ R_{j} \bigcap L_{i} \ne \varnothing & i>j \end{cases}\) ,又因为一定有 \(\begin{cases} R_{i} \bigcap L_{j}= \varnothing & i>j \\ R_{j} \bigcap L_{i} = \varnothing & i<j \end{cases}\) ,即 \((R_{i} \bigcap L_{j}) \bigcup (L_{i} \bigcap R_{j}) \ne \varnothing\)

\(S\) [ABC351G] Hash on Tree

\(T\) [ARC073F] Many Moves

\(U\) luogu P4719 【模板】"动态 DP"&动态树分治

\(V\) luogu P6348 [PA2011] Journeys

\(W\) luogu P2605 [ZJOI2010] 基站选址

\(X\) luogu P3521 [POI2011] ROT-Tree Rotations

数据结构2

开题顺序: \(BAHJCE\)

\(A\) luogu B3656 【模板】双端队列 1

  • list 代替 deque

  • 关于 dequelist 的空间问题,详见 关于deque和list

    点击查看代码
    list<int>q[1000001];
    int main()
    {
        int n,i,a,x;
        string pd;
        cin>>n;
        for(i=1;i<=n;i++)
        {
            cin>>pd;
            if(pd=="push_back")
            {
                cin>>a>>x;
                q[a].push_back(x);
            }
            if(pd=="pop_back")
            {
                cin>>a;
                if(q[a].empty()==0)
                {
                    q[a].pop_back();
                }
            }
            if(pd=="push_front")
            {
                cin>>a>>x;
                q[a].push_front(x);
            }
            if(pd=="pop_front")
            {
                cin>>a;
                if(q[a].empty()==0)
                {
                    q[a].pop_front();
                }
            }
            if(pd=="size")
            {
                cin>>a;
                cout<<q[a].size()<<endl;
            }
            if(pd=="front")
            {
                cin>>a;
                if(q[a].empty()==0)
                {
                    cout<<q[a].front()<<endl;
                }
            }
            if(pd=="back")
            {
                cin>>a;
                if(q[a].empty()==0)
                {
                    cout<<q[a].back()<<endl;
                }
            }
        }
        return 0;
    }
    

\(B\) luogu P1892 [BOI2003] 团伙

  • 考虑扩展域并查集,把一个点 \(x\) 拆成两个节点 \(x_{friend}\)\(x_{enemy}\)

  • \(x,y\) 是朋友,则合并 \(x_{friend},y_{friend}\) ;否则合并 \(x_{friend},y_{enemy}\)\(x_{enemy},y_{friend}\)

    点击查看代码
    int vis[2010];
    struct DSU
    {
    	int fa[2010];
    	void init(int n)
    	{
    		for(int i=1;i<=n;i++)
    		{
    			fa[i]=i;
    		}
    	}
    	int find(int x)
    	{
    		return fa[x]==x?x:fa[x]=find(fa[x]);
    	}
    	void merge(int x,int y)
    	{
    		x=find(x);
    		y=find(y);
    		if(x!=y)
    		{
    			fa[x]=y;
    		}
    	}
    }D;
    int main()
    {
    	int n,m,x,y,ans=0,i;
    	char pd;
    	cin>>n>>m;
    	D.init(2*n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd>>x>>y;
    		if(pd=='F')
    		{
    			D.merge(x,y);
    		}
    		else
    		{
    			D.merge(x,y+n);
    			D.merge(x+n,y);
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		ans+=(vis[D.find(i)]==0);
    		vis[D.find(i)]=1;		
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

\(C\) luogu P2502 [HAOI2006] 旅行

  • luogu P4234 最小差值生成树 的暴力做法,不妨钦定某一条边作为最小边在最终的生成树上。

  • 接着按边权大小枚举剩下的边直至 \(s,t\) 连通。

    点击查看代码
    struct node
    {
    	int from,to,w;
    }e[5010];
    bool cmp(node a,node b)
    {
    	return a.w<b.w;
    }
    int gcd(int a,int b)
    {
    	return b?gcd(b,a%b):a;
    }
    struct DSU
    {
    	int fa[510];
    	void init(int n)
    	{
    		for(int i=1;i<=n;i++)
    		{
    			fa[i]=i;
    		}
    	}
    	int find(int x)
    	{
    		return (fa[x]==x)?x:fa[x]=find(fa[x]);
    	}
    }D;
    int main()
    {
    	int n,m,s,t,x,y,d,i,j;
    	pair<int,int>ans=make_pair(1,0);
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>e[i].from>>e[i].to>>e[i].w;
    	}
    	cin>>s>>t;
    	sort(e+1,e+1+m,cmp);
    	for(i=1;i<=m;i++)
    	{
    		D.init(n);
    		for(j=i;j<=m;j++)
    		{
    			x=D.find(e[j].from);
    			y=D.find(e[j].to);
    			if(x!=y)
    			{
    				D.fa[x]=y;
    			}
    			if(D.find(s)==D.find(t))
    			{
    				if(ans.first*e[i].w>ans.second*e[j].w)
    				{
    					ans.first=e[j].w;
    					ans.second=e[i].w;
    				}
    				break;
    			}
    		}
    	}
    	if(ans.second==0)
    	{
    		cout<<"IMPOSSIBLE"<<endl;
    	}
    	else
    	{
    		if(ans.first%ans.second==0)
    		{
    			cout<<ans.first/ans.second<<endl;
    		}
    		else
    		{
    			d=gcd(ans.first,ans.second);
    			cout<<ans.first/d<<"/"<<ans.second/d<<endl;
    		}
    	}
    	return 0;
    }
    

\(D\) luogu P3402 可持久化并查集

\(E\) luogu P4768 [NOI2018] 归程

  • \(\{ a \}\) 建出 \(Kruskal\) 重构树(最大生成树),倍增找到合法点后子树内部的点都是开车可以到达的,取到 \(1\) 的距离的最小值即可。

    点击查看代码
    struct node
    {
    	ll nxt,to,w;
    }e[800010];
    ll head[200010],dis[400010],vis[200010],u[400010],v[400010],a[400010],p[400010],c[400010],fa[400010][25],f[400010],cnt=0;
    vector<ll>E[400010];
    void add(ll u,ll v,ll w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    bool cmp(ll x,ll y)
    {
    	return a[x]>a[y];
    }
    void dijkstra(ll s)
    {
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	priority_queue<pair<ll,ll> >q;
    	dis[s]=0;
    	q.push(make_pair(-dis[s],s));
    	while(q.empty()==0)
    	{
    		ll x=q.top().second;
    		q.pop();
    		if(vis[x]==0)
    		{
    			vis[x]=1;
    			for(ll i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;
    					q.push(make_pair(-dis[e[i].to],e[i].to));
    				}
    			}
    		}
    	}
    }
    struct DSU
    {
    	ll fa[400010];
    	void init(ll n)
    	{
    		for(ll i=1;i<=n;i++)
    		{
    			fa[i]=i;
    		}
    	}
    	ll find(ll x)
    	{
    		return fa[x]==x?x:fa[x]=find(fa[x]);
    	}
    }D;
    void kruskal(ll n,ll m)
    {
    	D.init(2*n);	
    	sort(p+1,p+1+m,cmp);
    	for(ll i=1,tot=n;i<=m&&tot<=2*n-1;i++)
    	{
    		ll x=D.find(u[p[i]]),y=D.find(v[p[i]]);
    		if(x!=y)
    		{
    			tot++;
    			c[tot]=a[p[i]];
    			D.fa[x]=D.fa[y]=tot;
    			E[tot].push_back(x);
    			E[tot].push_back(y);
    		}
    	}
    }	
    void dfs(ll x,ll father)
    {
    	f[x]=dis[x];
    	fa[x][0]=father;
    	for(ll i=1;i<=20;i++)
    	{
    		fa[x][i]=fa[fa[x][i-1]][i-1];
    	}
    	for(ll i=0;i<E[x].size();i++)
    	{
    		if(E[x][i]!=father)
    		{
    			dfs(E[x][i],x);
    			f[x]=min(f[x],f[E[x][i]]);
    		}
    	}
    }
    ll ask(ll x,ll val)
    {
    	ll rt=x;
    	for(ll i=20;i>=0;i--)
    	{
    		if(fa[rt][i]!=0&&c[fa[rt][i]]>val)
    		{
    			rt=fa[rt][i];
    		}
    	}
    	return rt;
    }
    int main()
    {
    	ll t,n,m,w,q,k,s,x,y,ans,i,j;
    	scanf("%lld",&t);
    	for(j=1;j<=t;j++)
    	{
    		cnt=ans=0;
    		memset(e,0,sizeof(head));
    		memset(head,0,sizeof(head));
    		memset(c,0,sizeof(c));
    		scanf("%lld%lld",&n,&m);
    		for(i=1;i<=2*n-1;i++)
    		{
    			E[i].clear();
    		}
    		for(i=1;i<=m;i++)
    		{
    			scanf("%lld%lld%lld%lld",&u[i],&v[i],&w,&a[i]);
    			add(u[i],v[i],w);
    			add(v[i],u[i],w);
    			p[i]=i;
    		}
    		dijkstra(1);
    		kruskal(n,m);
    		dfs(2*n-1,0);
    		scanf("%lld%lld%lld",&q,&k,&s);
    		for(i=1;i<=q;i++)
    		{
    			scanf("%lld%lld",&x,&y);
    			x=(x+k*ans-1)%n+1;
    			y=(y+k*ans)%(s+1);
    			ans=f[ask(x,y)];
    			printf("%lld\n",ans);
    		}
    	}
    	return 0;
    }
    

\(F\) luogu P5854 【模板】笛卡尔树

\(G\) luogu P2081 [NOI2012] 迷失游乐园

\(H\) luogu P4381 [IOI2008] Island

\(I\) CF1713E Cross Swapping

\(J\) HDU6403 Card Game

\(K\) luogu P6453 [COCI2008-2009#4] PERIODNI

\(L\) luogu P3765 总统选举

\(M\) CF1920F2 Smooth Sailing (Hard Version)

\(N\) CF1687C Sanae and Giant Robot

\(O\) [AGC003E] Sequential operations on Sequence

\(P\) luogu P3452 [POI2007] BIU-Offices

posted @ 2024-10-05 07:01  hzoi_Shadow  阅读(51)  评论(2编辑  收藏  举报
扩大
缩小