zoukankan      html  css  js  c++  java
  • [提高组集训2021] 模拟赛6

    风之轨迹「miracle」

    题目描述

    \(n\) 个点 \(m\) 条边的有向无环图,定义路径长度为路径上边的数量。

    问删掉一个点之后所得到的最大的路径长度,并且要求你输出删除的这个点(有多解输出最小的一个)

    \(n\leq 5\times 10^5,m\leq 10^6\)

    解法

    为了便于讨论我们建立超级起点连向所有点,所有点再连向超级终点。

    首先有一个基本的 \(\tt observation\):删去 \(x\) 之后的最长路就是走起点拓扑序 \(<x\),终点拓扑序 \(>x\) 的边得到的路径最大值。

    那么我们可以预处理出经过每条边的路径最大值(正图和反图跑拓扑),然后类似扫描线,离开一个点的时候加入所有边到 \(\tt set\),询问之后删除这个点的所有边,时间复杂度 \(O(m\log m)\)

    总结

    删除一个点并不需要真的删去,你可以转化成强制不经过它。

    #include <cstdio>
    #include <iostream>
    #include <queue>
    #include <set>
    using namespace std; 
    const int M = 2000005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,ans,id;
    struct node
    {
    	int u,v,c;
    	bool operator < (const node &b) const
    	{
    		if(c==b.c && u==b.u) return v<b.v;
    		if(c==b.c) return u<b.u;
    		return c>b.c;
    	}
    };set<node> s;
    struct graph
    {
    	int tot,cnt,f[M],dp[M],o[M],d[M];
    	struct edge{int v,next;}e[M<<1];
    	void add(int u,int v)
    	{
    		d[v]++;
    		e[++tot]=edge{v,f[u]},f[u]=tot;
    	}
    	void tpsort()
    	{
    		queue<int> q;
    		for(int i=0;i<=n+1;i++)
    			if(!d[i]) q.push(i);
    		while(!q.empty())
    		{
    			int u=q.front();q.pop();
    			o[cnt++]=u;
    			for(int i=f[u];i;i=e[i].next)
    			{
    				int v=e[i].v;d[v]--;
    				dp[v]=max(dp[v],dp[u]+1);
    				if(!d[v]) q.push(v);
    			}
    		}
    	}
    }G1,G2;
    signed main()
    {
    	freopen("miracle.in","r",stdin);
    	freopen("miracle.out","w",stdout);
    	n=read();m=read();ans=n;
    	for(int i=1;i<=m;i++)
    	{
    		int u=read(),v=read();
    		G1.add(u,v);
    		G2.add(v,u);
    	}
    	for(int i=1;i<=n;i++)
    	{
    		G1.add(0,i),G2.add(i,0);
    		G1.add(i,n+1),G2.add(n+1,i);
    	}
    	G1.tpsort();G2.tpsort();
    	for(int i=0;i<=n+1;i++)
    	{
    		int x=G1.o[i];
    		for(int j=G2.f[x];j;j=G2.e[j].next)
    		{
    			int v=G2.e[j].v;
    			s.erase(node{v,x,G2.dp[x]+G1.dp[v]+1});
    		}
    		if(!s.empty())
    		{
    			int c=s.begin()->c-2;
    			if(c<ans || (c==ans && x<id))
    				ans=c,id=x;
    		}
    		for(int j=G1.f[x];j;j=G1.e[j].next)
    		{
    			int v=G1.e[j].v;
    			s.insert(node{x,v,G2.dp[v]+G1.dp[x]+1});
    		}
    	}
    	printf("%d %d\n",id,ans);
    }
    

    羽未「umi」

    题目描述

    给定一个长度为 \(n\) 的颜色序列 \(a_i\),我们称序列是好的当且仅当所有颜色再序列上的出现位置连续。

    我们可以把一种颜色整体修改成另一种颜色,问最终序列和原序列最小不同的位置数量。

    你还需要支持 \(m\) 次修改,每次修改一个位置的颜色,然后给出答案。

    \(n,m\leq 2\cdot 10^5\)

    解法

    考虑把原序列分段,那么每一段的代价就是 长度\(-\)最多的出现次数。然后考虑分段方式是确定的,设 \(b_i\) 表示有多少 \([l_c,r_c]\) 覆盖了 \([i,i+1]\),所有 \(b_i=0\) 的位置都是分界点。

    问题变成了维护所有 \(0\) 之间的最大值,\(b\) 数组需要支持区间加法,权值数组需要支持单点修改。

    我们可以转而维护 \(b\) 数组最小值之间的最大值之和,这样区间加法就对它没用影响了。上传的时候需要先讨论左右儿子最小值的关系,然后类似最大子段的方式合并即可,时间复杂度 \(O(n\log n)\)

    #include <cstdio>
    #include <set>
    using namespace std;
    const int M = 200005;
    const int up = 200000;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,a[M],b[M],fl[4*M];set<int> s[M];
    struct node
    {
    	int mn,lm,rm,mx,sum;
    	node operator + (const node &b) const
    	{
    		node r;
    		if(mn>b.mn)
    		{
    			r.mn=b.mn;r.lm=max(mx,b.lm);
    			r.rm=b.rm;r.mx=max(mx,b.mx);
    			r.sum=b.sum;return r;
    		}
    		if(mn<b.mn)
    		{
    			r.mn=mn;r.lm=lm;
    			r.rm=max(rm,b.mx);r.mx=max(mx,b.mx);
    			r.sum=sum;return r;
    		}
    		r.mn=mn;r.lm=lm;r.rm=b.rm;
    		r.mx=max(mx,b.mx);
    		r.sum=sum+b.sum+max(rm,b.lm);
    		return r;
    	}
    }t[4*M];
    void fuck(int x,int c)
    {
    	if(!x) return ;
    	t[x].mn+=c;fl[x]+=c;
    }
    void down(int i)
    {
    	if(fl[i])
    	{
    		fuck(i<<1,fl[i]);
    		fuck(i<<1|1,fl[i]);
    		fl[i]=0;
    	}
    }
    void ins(int i,int l,int r,int id)
    {
    	if(l==r)
    	{
    		t[i].lm=b[id];t[i].rm=b[id+1];
    		t[i].mx=max(b[id],b[id+1]);
    		return ;
    	}
    	int mid=(l+r)>>1;down(i);
    	if(mid>=id) ins(i<<1,l,mid,id);
    	else ins(i<<1|1,mid+1,r,id);
    	t[i]=t[i<<1]+t[i<<1|1]; 
    }
    void add(int i,int l,int r,int L,int R,int c)
    {
    	if(L>r || l>R) return ;
    	if(L<=l && r<=R) {fuck(i,c);return ;}
    	int mid=(l+r)>>1;down(i);
    	add(i<<1,l,mid,L,R,c);
    	add(i<<1|1,mid+1,r,L,R,c);
    	t[i]=t[i<<1]+t[i<<1|1];
    }
    void upd(int x)
    {
    	if(!s[x].size()) return ;
    	int p=*s[x].begin();b[p]=s[x].size();
    	add(1,0,n,p,*s[x].rbegin()-1,1);
    	ins(1,0,n,p-1);ins(1,0,n,p);
    }
    void del(int x)
    {
    	if(!s[x].size()) return ;
    	int p=*s[x].begin();b[p]=0;
    	add(1,0,n,p,*s[x].rbegin()-1,-1);
    	ins(1,0,n,p-1);ins(1,0,n,p);
    }
    signed main()
    {
    	freopen("umi.in","r",stdin);
    	freopen("umi.out","w",stdout);
    	n=read();m=read();
    	for(int i=1;i<=n;i++)
    	{
    		a[i]=read();
    		s[a[i]].insert(i);
    	}
    	for(int i=1;i<=up;i++) upd(i);
    	printf("%d\n",n-t[1].sum);
    	while(m--)
    	{
    		int x=read(),y=read();
    		if(a[x]==y)
    		{
    			printf("%d\n",n-t[1].sum);
    			continue;
    		}
    		del(a[x]);del(y);
    		s[a[x]].erase(x);
    		s[y].insert(x);
    		upd(a[x]);upd(y);a[x]=y;
    		printf("%d\n",n-t[1].sum);
    	}
    }
    
  • 相关阅读:
    ASP.NET Repeater的用法初探
    ADO.NET 数据查询和数据操作
    ASP.NET 一般处理程序基础1(Get Post 表单提交 Http协议 Nvelocity模板引擎)
    接口继承
    《需求工程》阅读笔记2
    《需求工程》阅读笔记1
    Python3.0中的strip方法失效问题以及re.sub方法无法执行问题
    使用Python爬取豆瓣电影详细数据
    《软件方法》阅读笔记——3
    基于layui实现了将查询出的数据分页显示
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15569391.html
Copyright © 2011-2022 走看看