1.数据结构

数据结构1

开题顺序: BXGEWDJVNCFQKPLAHR

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

B luogu P1637 三元上升子序列

C luogu P6492 [COCI2010-2011#6] STEP

  • L 视作 0R 视作 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 使得对于任意一条边 uvvalu=valvuv 是重边,否则是轻边。

  • 此时操作 1 等价于将 uv 上的点的权值都赋成一个统一的值;操作 2 等价于查询 uv 上相邻两点权值相同的无序点对,做法同 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 的节点一定是 xy2 级祖先的 y2 的子孙。

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

    • 因为直接查询 xy2 级祖先的子树中深度和 x 的深度相等的情况下,会将 xy21 级祖先的 y21 级子孙也统计到,需要减去这部分贡献。
  • 对于回溯操作,操作树或主席树维护即可。其中后者的空间复杂度多带一个 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

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

  • i[1,n],posai=i

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

  • 具体地,设 fl,r 表示值在 [l,r] 中的数组成区间的个数, Sl,r 表示值在 [l,r] 中的数的下标组成的集合。

  • 考虑右端点由 r1 变成 rf 的影响。首先不难有 l[1,r],posrSl,r ,故假设 r 会单独成一段,即 fl,r+=1(l[1,r]) ;接着若 aposr1<rl[1,aposr1],posrSl,rposr1Sl,r ,故 posr 可以和 posr1 拼起来,使区间个数少一,即 fl,r=1(l[1,aposr1]) ;最后若 aposr+1<rl[1,aposr+1],posrSl,rposr+1Sl,r ,故 posr 可以和 posr+1 拼起来,使区间个数少一,即 fl,r=1(l[1,aposr+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 减去 {aii} 的最长不下降子序列长度个数。

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

  • i[1,n],bi=aii

  • fi,0/1 表示以 i 结尾的前缀中没有/有删除过的数时(删除的这个数仅能 [1,i1] ,能够在最终保留但不参与运算)最多能保留的数的个数,状态转移方程为 {fi,0=maxj=1i1{[bjbi]×(fj,0+1)}fi,1=max(maxj=1i1{[bjbi]×(fj,1+1)},maxj=1i2{[bjbi+1]×(fj,0+1)}) ,边界为 {f1,0=1f1,1=0

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

  • 最终,有 max(n1maxi=1n{fi,0,fi,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

  • 容易有 [FibnFibn+1]=[Fibn1Fibn]×[0111]

  • 建树时暴力算斐波那契数,修改时维护 [0111] 的乘方标记,区间和维护矩阵加法即可。

    • 本质上应用了矩阵的结合律 A×B+A×C=A×(B+C)
  • 为方便重载运算符,将原 F 矩阵写作 [FibnFibn+100]

    点击查看代码
    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)E 当且仅当 |ij|[li+lj,ri+rj] ,即 {i+lijlji+rijrji<jj+ljilij+rjirii>j ,即 {[i+li,i+ri][jlj,jrj]i<j[j+lj,j+rj][ili,iri]i>j
  • i[1,n],Li=[ili,iri],Ri=[i+li,i+ri] ,上述条件转化为 {RiLji<jRjLii>j ,又因为一定有 {RiLj=i>jRjLi=i<j ,即 (RiLj)(LiRj)

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 拆成两个节点 xfriendxenemy

  • x,y 是朋友,则合并 xfriend,yfriend ;否则合并 xfriend,yenemyxenemy,yfriend

    点击查看代码
    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 @   hzoi_Shadow  阅读(83)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
扩大
缩小
点击右上角即可分享
微信分享提示