zoukankan      html  css  js  c++  java
  • 【题解】Informacije [COCI2012]

    【题解】Informacije [COCI2012]

    传送门:官方题面

    【题目描述】

    有一个长度为 (n) 的 序列 (a)(由 ([1,n]) 中的数组成,且每个数只会出现一次),现给出两个整数 (n,m)(m) 个关于 (a) 的描述,格式如下:

    (1 l r v) 表示 (max{a[l],a[l+1]...a[r]}=v),

    (2 l r v) 表示 (min{a[l],a[l+1]...a[r]}=v)

    请输出一个满足上面 (m) 个描述的序列,如果多种答案,输出任意一种,无解则输出 (-1)

    【样例】

    样例输入:
    3 2
    1 1 1 1
    2 2 2 2
    
    样例输出:
    1 2 3
    
    样例输入:
    4 2
    1 1 1 1
    2 3 4 1
    
    样例输出:
    -1
    
    样例输入:
    5 2
    1 2 3 3
    2 4 5 4
    
    样例输出:
    1 2 3 4 5
    

    【数据范围】

    (100 \%:) (1 leqslant n leqslant 200,) (0 leqslant m leqslant 40000)


    【分析】

    (n leqslant 200),一开始只是觉得可以写 (n^3) 的算法,比如矩阵乘法之类的,但看到 (m leqslant 40000) 时,瞬间想到建一张完全图跑图论。事实证明这一直觉是正确的。

    (pan[i][j]) 表示整数 (i) 是否可以填在 (j) 这个位置(只需要满足给出的 (m) 个条件即可)。

    如果 (pan[i][j])(1),那么 (i)(j) 连一条有向边,然后跑一遍二分图最大匹配,(match) 数组即为答案。

    匈牙利算法的时间复杂度为:(O(|V|*|E|)),其中 (|E| leqslant |V|^2)(200) 个点的完全图完全不是问题。

    如何求 (pan) 数组?

    最开始 (yy) 了一种 (mn) 的预处理方法:

    ((1).) (Lw[x],Rw[x]) 分别表示整数 (x) 必须要放的位置所在区间左右端点。
    对于所有的 (l,r,v)(Lw[v]=max{l,Lw[v]},Rw[v]=min{r,Rw[v]})

    ((2).) (Ls[i],Rs[i]) 分别表示位置 (i) 可放置的整数范围。
    对于 (1,l,r,v)(Rs[i]=min{v,Rs[i]} (i in [l,r]))
    对于 (2,l,r,v)(Ls[i]=max{v,Ls[i]} (i in [l,r]))

    但仔细想想觉得不太对,所以就改成了 (mn^2) 的暴力枚举,本以为会超时,结果加了剪枝后居然轻松跑过了。

    后来发现官方题解给的就是 (mn) 的做法,而且和我之前想的一模一样。。。

    【Code】

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #define Re register int
    using namespace std;
    const int N=203,M=40003,inf=2e9;
    int n,m,op[M],L[M],R[M],val[M],pan[N][N];
    int o,ans,vis[N],head[N],match[N];
    struct QAQ{int to,next;}a[N*N];
    inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    inline int judge(Re i,Re L,Re R){//判断在[L,R]这个区间内是否有1
    	for(Re j=L;j<=R;++j)if(pan[i][j])return 1;
    	return 0;
    }
    inline void add_(Re i,Re L,Re R){//将[L,R]全部变为0
    	for(Re j=L;j<=R;++j)pan[i][j]=0;
    }
    inline void add(Re i,Re L,Re R){//将[L,R]以外的全部变为0
    	for(Re j=1;j<L;++j)pan[i][j]=0;
    	for(Re j=R+1;j<=n;++j)pan[i][j]=0;
    }
    inline void Print(){
    	for(Re i=1;i<=n;++i){
    		printf("pan[%d]: ",i);
    		for(Re j=1;j<=n;++j)if(pan[i][j])printf("%d ",j);
    		puts("");
    	}
    	puts("");
    }
    inline int sakura(){
    	for(Re i=1;i<=m;++i){
    		if(op[i]>1){//L[i]~R[i]的最小值为val[i]
    			if(!judge(val[i],L[i],R[i]))return 0;
    			add(val[i],L[i],R[i]);
    			for(Re j=1;j<val[i];++j){//比val小的数 
    				Re flag1=judge(j,1,L[i]-1),flag2=judge(j,R[i]+1,n);
    				if(!flag1&&!flag2)return 0;//如果没有可放的位置就直接return
    				else if(flag1&&!flag2)add(j,1,L[i]-1);//删掉左边
    				else if(flag2&&!flag1)add(j,R[i]+1,n);//删掉右边
    				else add_(j,L[i],R[i]);//删掉左右两边
    			}
    		}
    		else{//L[i]~R[i]的最大值为val[i]
    			if(!judge(val[i],L[i],R[i]))return 0;
    			add(val[i],L[i],R[i]);
    			for(Re j=val[i]+1;j<=n;++j){//比val大的数 
    				Re flag1=judge(j,1,L[i]-1),flag2=judge(j,R[i]+1,n);
    				if(!flag1&&!flag2)return 0;
    				else if(flag1&&!flag2)add(j,1,L[i]-1);
    				else if(flag2&&!flag1)add(j,R[i]+1,n);
    				else add_(j,L[i],R[i]);
    			}
    		}
    	}
    	return 1;//最后还要return 1
    }
    inline int dfs(Re x){
    	for(Re i=head[x],to;i;i=a[i].next)
    		if(!vis[to=a[i].to]){
    			vis[to]=1;
    			if(!match[to]||dfs(match[to])){
    				match[to]=x;return 1;
    			}
    		}
    	return 0;
    }
    int main(){
    	// freopen("informacije.in","r",stdin);
    	// freopen("informacije.out","w",stdout);
    	in(n),in(m);
    	for(Re i=1;i<=m;++i)in(op[i]),in(L[i]),in(R[i]),in(val[i]);
    	for(Re i=1;i<=n;++i)
    		for(Re j=1;j<=n;++j)
    			pan[i][j]=1;
    	if(!sakura())puts("-1");
    	else{
    //		Print();
    		for(Re i=1;i<=n;++i)
    			for(Re j=1;j<=n;++j)
    				if(pan[i][j])add(i,j);
    		for(Re i=1;i<=n;++i){
    			memset(vis,0,sizeof(vis));
    			if(!dfs(i))return !puts("-1");
    		}
    		for(Re i=1;i<=n;++i)printf("%d ",match[i]);
    	}
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
    
  • 相关阅读:
    Debian apt-get 无法补全
    Python 字典排序
    Python 替换字符串
    Debian 7 64位安装 wine
    Python Virtualenv 虚拟环境
    ASP.NET MVC ModelState
    Oracle存储过程写法
    利用ODBC从SQLServer向Oracle中导数据
    web自定义控件UserControl
    工作笔记
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/11733352.html
Copyright © 2011-2022 走看看