zoukankan      html  css  js  c++  java
  • [CSP校内集训]替换游戏(tarjan+离散化)

    题意

    给一个范围([0,n]),有两种变换方式,(+k)或者给定的(m)(x->y),但必须保证变换前后的数始终在范围内,给一个数(x),求出它一直变换下去(注意不能得到了一个数之后返回上一步)可以得到的所有数的和的最大值((nleq 10^8 , mleq 10^5 , kleq n)),多组询问

    思路

    对两种方式建边,显然一个强连通分量中的点是都可以选到的,所以相当于多次询问求出DAG中从某个点出发的最长链,建反边拓扑即可

    但是这道题的点数是(10^8)级别的,考虑到大多数连边都是第一种很规则的连边,所以有很多点是没有必要的,考虑离散化

    将所有数按照对(k)取模的值分为(k)组,按照第一种方式连边后形成(k)条互不相交的链

    将所有的第二种建边和询问用到的点离线下来离散化,即这些点需要单独考虑,这些点将链分为了很多小段,把每一个小段变成一个点

    这时就可以发现点数已经与(n)无关了(有(O(m+q))级别的点数),连接这些点后就可以像上面的暴力一样做了

    P.S. 点权请自行用等差数列解决

    #include<bits/stdc++.h>
    #define N 600005
    #define Min(x,y) ((x)<(y)?(x):(y))
    #define Max(x,y) ((x)>(y)?(x):(y))
    using namespace std;
    typedef long long ll; 
    int n,m,k,ndsum;
    int dfn[N],low[N],cp,col,color[N];//tarjan 
    int st[N],top,rd[N];//拓扑 
    int b[N],c[N],len;//离散化特例 
    
    ll sum[N],val[N],f[N];
    vector<int> son[N];//新图 
    
    struct E { int u,v; } e[N];
    int q[N];//离线询问 和 边 
    
    struct Edge
    {
    	int next,to;
    }edge[N<<2];int head[N],cnt;
    void add_edge(int from,int to)
    {
    	edge[++cnt].next=head[from];
    	edge[cnt].to=to;
    	head[from]=cnt;
    }
    
    template <class T>
    void read(T &x)
    {
    	char c;int sign=1;
    	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
    }
    void tarjan(int rt)
    {
    	dfn[rt]=low[rt]=++cp;
    	st[++top]=rt;
    	for(int i=head[rt];i;i=edge[i].next)
    	{
    		int v=edge[i].to;
    		if(!dfn[v])
    		{
    			tarjan(v);
    			low[rt]=Min(low[rt],low[v]);
    		}
    		else if(!color[v]) low[rt]=Min(low[rt],dfn[v]);
    	}
    	if(dfn[rt]==low[rt])
    	{
    		color[rt]=++col;
    		sum[col]+=val[rt];
    		while(st[top]!=rt)
    		{
    			sum[col]+=val[st[top]];
    			color[st[top--]]=col;
    		}
    		--top;
    	}
    }
    void init()
    {
    	for(int i=1;i<=ndsum;++i)
    	{
    		for(int j=head[i];j;j=edge[j].next)
    		{
    			int v=edge[j].to;
    			if(color[i]!=color[v])
    			{
    				son[color[v]].push_back(color[i]);
    				++rd[color[i]];
    			}
    		}
    	}
    	queue<int> q;
    	for(int i=1;i<=col;++i) if(!rd[i]) q.push(i),f[i]=sum[i];
    	while(!q.empty())
    	{
    		int u=q.front(); q.pop();
    		for(int i=0,t=son[u].size();i<t;++i)
    		{
    			int v=son[u][i];
    			f[v]=Max(f[v],f[u]+sum[v]);
    			if(--rd[v]==0) q.push(v);
    		}
    	}
    }
    bool cmp(int x,int y)
    {
    	return (b[x]%k==b[y]%k ? b[x]<b[y] : b[x]%k<b[y]%k);//按颜色排序 
    }
    int get_max(int n,int x)//找到满足 <= n 的 最大的 mod k==x的数 
    {
    	int ret=n/k*k+x;
    	return ret-(ret>n)*k;
    }
    ll get_sum(int x,int y)//求(x,y]的和 
    {
    	return (ll)(y-x)*(x+y+k)/(2*k);
    }
    void work()
    {
    	for(int i=1;i<=len;++i) c[i]=i;
    	sort(c+1,c+len+1,cmp);//连基础边 
    	for(int i=1;i<=len;++i)
    	{
    		val[c[i]]=b[c[i]];
    		int co=b[c[i]]%k;
    		if(i==len||b[c[i]]%k!=b[c[i+1]]%k)//一个颜色的最后一个点 
    		{
    			int maxx=get_max(n,co);
    			if(maxx!=b[c[i]])
    			{
    				++ndsum;
    				add_edge(c[i],ndsum);
    				val[ndsum]=get_sum(b[c[i]],maxx);
    			}
    		}
    		else
    		{
    			int maxx=get_max(b[c[i+1]]-1,co);
    			if(maxx>b[c[i]])
    			{
    				++ndsum;
    				add_edge(c[i],ndsum);
    				add_edge(ndsum,c[i+1]);
    				val[ndsum]=get_sum(b[c[i]],maxx);
    			}
    			else add_edge(c[i],c[i+1]);
    		}
    	}
    	
    	for(int i=1;i<=m;++i) add_edge(e[i].u,e[i].v); //连特殊边  
    	for(int i=1;i<=ndsum;++i) if(!dfn[i]) tarjan(i);
    }
    int main()
    {
    	freopen("substitution.in","r",stdin);
    	freopen("substitution.out","w",stdout);
    	//读入 
    	read(n);read(m);read(k);
    	for(int i=1;i<=m;++i)
    	{
    		read(e[i].u);read(e[i].v);
    		b[++len]=e[i].u;
    		b[++len]=e[i].v;
    	}
    	int T; read(T);
    	for(int i=1;i<=T;++i)
    	{
    		read(q[i]);
    		b[++len]=q[i];
    	}
    	
    	//离散化 
    	sort(b+1,b+len+1);
    	len=unique(b+1,b+len+1)-b-1;
    	ndsum=len;
    	
    	for(int i=1;i<=m;++i)
    	{
    		e[i].u=lower_bound(b+1,b+len+1,e[i].u)-b;
    		e[i].v=lower_bound(b+1,b+len+1,e[i].v)-b;
    	}
    	for(int i=1;i<=T;++i) q[i]=lower_bound(b+1,b+len+1,q[i])-b;
    	
    	work();
    	init();
    	
    	for(int i=1;i<=T;++i) printf("%lld
    ",f[color[q[i]]]);
    	return 0;
    }
    
  • 相关阅读:
    IIS6.0中,使用其它用户运行IIS
    c++构造函数详解
    Java创建文件夹及文件
    三大范式及存储方式
    对新型软件程序语言及架构的一点讨论
    C++虚函数解析
    验收测试驱动开发:ATDD实例详解
    什么是父路径及开启的方法
    note:全局配置visual studio 2010 头文件目录
    怎么把二级域名重定向到主域名
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11655138.html
Copyright © 2011-2022 走看看