zoukankan      html  css  js  c++  java
  • bzoj2788-Festival

    题意

    (n) 个变量,有两种限制,分别有 (m_1,m_2) 种。限制如下:

    • (a_x+1=a_y)
    • (a_xle a_y)

    ({x_i}) 集合的大小。(nle 600,m_1+m_2le 10^5)

    分析

    求集合大小其实就是最多有多少个变量不相同。看到这种变量的加减约束问题,首先想到了差分约束。

    建边,若 (a_x+1=a_y) ,那么 (a_x+1ge a_y,a_y-1ge a_x) ,所以连边 ((x,y,1),(y,x,-1))

    (a_xle a_y) 那么连边 ((y,x,0))

    先考虑一些特殊的情况。若出现非0的环,就说明出现了矛盾,排除掉这种情况。

    对这个图缩环,那么形成了一个DAG,其中的边权值都为0,否则一定有一条跟它反向的边,那么就出现了一个新的环,与DAG定义不符。

    那么每一个新点所代表的不同强连通分量之间的点的取值是不影响的。我们只需要考虑每一个的最多取值,全部加起来即可,因为总可以构造出不冲突的方案。

    在一个强连通块中走出一条路径,就对应着一组可能的解。从第一个点开始标号,经过边权为1就加一,经过边权为-1就减一,经过边权为0就不变,这样我们得到的解一定是合法的,因为我们满足了所有的要求,而且我们已经排除了正权回路,即不会出现任何矛盾。同时我们得到的解一定是这条路径上从小到大标号中最优的,因为标号必定是连续的。同时,所有的解都能够在图上找到一条路径与之对应。

    若这条路径的开头或结尾是-1,那么我们去掉这个,答案变优了,称这个操作为一次优化,那么对于一条路径,不断地优化,然后除去开头就是((1,-1)) 和结尾为 ((-1,1)) 的,这样答案要么变优,要么不变。

    最终我们得到了一条路径,这条路径是某两个点间的最短路,它的权值最大。这个权值的意义其实是终点减去起点的值的最大值。由于边权只有 ({-1,0,1}) ,所以每一个都会被取到。

    综上,一个强连通块中的最多取值就是最短路中最长的+1 。

    代码

    发现之前的Tarjan写的不太对,ins[x] 数组应该在弹栈的时候标记为 false ,而不是退出dfs的时候。

    #include<bits/stdc++.h>
    using namespace std;
    inline char nchar() {
    	static const int bufl=1<<20;
    	static char buf[bufl],*a,*b;
    	return a==b && (b=(a=buf)+fread(buf,1,bufl,stdin),a==b)?EOF:*a++;
    }
    inline int read() {
    	int x=0,f=1;
    	char c=nchar();
    	for (;!isdigit(c);c=nchar()) if (c=='-') f=-1;
    	for (;isdigit(c);c=nchar()) x=x*10+c-'0';
    	return x*f;
    }
    const int maxn=605;
    int n,d[maxn][maxn],col[maxn],cs=0,inf,ans[maxn];
    int dfn[maxn],low[maxn],sta[maxn],top=0,dfx=0;
    bool ins[maxn],mp[maxn][maxn];
    inline void Max(int &x,int y) {x=max(x,y);}
    inline void Min(int &x,int y) {x=min(x,y);}
    void tarjan(int x) {
    	dfn[x]=low[x]=++dfx;
    	sta[++top]=x;
    	ins[x]=true;
    	for (int i=1;i<=n;++i) if (i!=x && mp[x][i]) {
    		if (!dfn[i]) tarjan(i),Min(low[x],low[i]); else 
    		if (ins[i]) Min(low[x],dfn[i]);
    	}
    	if (dfn[x]==low[x]) {
    		++cs;
    		int v;
    		do col[v=sta[top--]]=cs,ins[v]=false; while (v!=x);
    	}
    }
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("test.in","r",stdin);
    #endif
    	n=read();
    	memset(d,0x3f,sizeof d);
    	inf=d[0][0];
    	for (int i=1;i<=n;++i) d[i][i]=0;
    	int m1=read(),m2=read();
    	while (m1--) {
    		int x=read(),y=read();
    		Min(d[x][y],1);
    		Min(d[y][x],-1);
    		mp[x][y]=mp[y][x]=true;
    	}
    	while (m2--) {
    		int x=read(),y=read();
    		Min(d[y][x],0);
    		mp[y][x]=true;
    	}
    	for (int k=1;k<=n;++k) for (int i=1;i<=n;++i) if (d[i][k]!=inf) for (int j=1;j<=n;++j) if (d[k][j]!=inf) Min(d[i][j],d[i][k]+d[k][j]);
    	for (int i=1;i<=n;++i) if (d[i][i]<0) puts("NIE"),exit(0);
    	for (int i=1;i<=n;++i) if (!dfn[i]) tarjan(i);
    	fill(ans+1,ans+cs+1,1);
    	for (int i=1;i<=n;++i) for (int j=1;j<=n;++j) if (i!=j && col[i]==col[j]) Max(ans[col[i]],d[i][j]+1);
    	printf("%d
    ",accumulate(ans+1,ans+cs+1,0));
    	return 0;
    }
    
  • 相关阅读:
    文件的序列化和反序列化
    三个小功能,游戏倒计时,文件的序列化和反序列化,txt文档的读取和写入
    Unity 中Debug打印的全局注释方式和重写
    导航制作的几个步骤
    Unity中删除文件目录下的所有文件和查看文件里面的内容
    VS2017一些小技巧
    在Unity中图标进行鼠标图标更换
    Electron-Vue 使用 oss 实现上传、下载
    Electron-Vue 调用本地数据库
    构建 Electron-Vue 脚手架项目
  • 原文地址:https://www.cnblogs.com/owenyu/p/7382742.html
Copyright © 2011-2022 走看看