• 【BZOJ】1064: [Noi2008]假面舞会(判环+gcd+特殊的技巧)


    http://www.lydsy.com/JudgeOnline/problem.php?id=1064

    表示想到某一种情况就不敢写下去了。。。。

    就是找环的gcd。。。好可怕。。

    于是膜拜了题解。。

    和我想的差不多。。

    首先发现这3种情况:

    1、单链或者几条单链任意两条只相交于连续的一段的单链块。则最大的答案是$sum |单链| + sum |单链块种最长的链|$,最小答案是3。

    2、环。环的长度是最大答案的倍数,因此我们取gcd,也可以看做是标号差(第一次访问和第二次访问的标号差)。

    3、单链相交于至少两个不同的连续的一段。他们标号差(第一次访问和第二次访问的标号差)是最大答案的倍数。

    最大答案为所有情况的最大答案的gcd。最小答案如果只有第1种情况,则为3,否则为最大答案的>=3的最小因数。

    如果我们直接做的话很麻烦,可是怎么算情况2和情况3呢?

    解决方法非常巧妙!http://hi.baidu.com/lydrainbowcat/item/f3fdc53164770bd76d15e980

    这是有向图,我们把它变成无向图,那么不就可以从任意一个点开始都能得到整个环大小和情况3的标号差了吗..

    这样一条边权值为1,反向边则权值为-1.

    这样情况二和情况三就是标号差的gcd。

    而情况1就能在上述方法处理情况2的时候找出最长链的长度即可,即标号max{num}-min{num}+1

    最后答案分类讨论一下。

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <string>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <set>
    #include <map>
    using namespace std;
    typedef long long ll;
    #define pii pair<int, int>
    #define mkpii make_pair<int, int>
    #define pdi pair<double, int>
    #define mkpdi make_pair<double, int>
    #define pli pair<ll, int>
    #define mkpli make_pair<ll, int>
    #define rep(i, n) for(int i=0; i<(n); ++i)
    #define for1(i,a,n) for(int i=(a);i<=(n);++i)
    #define for2(i,a,n) for(int i=(a);i<(n);++i)
    #define for3(i,a,n) for(int i=(a);i>=(n);--i)
    #define for4(i,a,n) for(int i=(a);i>(n);--i)
    #define CC(i,a) memset(i,a,sizeof(i))
    #define read(a) a=getint()
    #define print(a) printf("%d", a)
    #define dbg(x) cout << (#x) << " = " << (x) << endl
    #define error(x) (!(x)?puts("error"):0)
    #define printarr2(a, b, c) for1(_, 1, b) { for1(__, 1, c) cout << a[_][__]; cout << endl; }
    #define printarr1(a, b) for1(_, 1, b) cout << a[_] << '	'; cout << endl
    inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; }
    inline const int max(const int &a, const int &b) { return a>b?a:b; }
    inline const int min(const int &a, const int &b) { return a<b?a:b; }
    
    const int N=100015;
    int ihead[N], cnt, m, n, num[N], q[N], vis[N], cir, sum, ans;
    struct dat { int next, to, w; }e[N*10*2];
    void add(int u, int v, int w) { e[++cnt].next=ihead[u]; ihead[u]=cnt; e[cnt].to=v; e[cnt].w=w; }
    inline const int gcd(const int &a, const int &b) { return b?gcd(b, a%b):a; }
    
    int main() {
    	read(n); read(m);
    	for1(i, 1, m) { int u=getint(), v=getint(); add(u, v, 1); add(v, u, -1); }
    	for1(k, 1, n) if(!vis[k]) {
    		int front=0, tail=0;
    		q[tail++]=k;
    		int mx=0, mn=0;
    		while(front!=tail) {
    			int x=q[front++]; if(front==N) front=0;
    			mx=max(mx, num[x]);
    			mn=min(mn, num[x]);
    			for(int i=ihead[x]; i; i=e[i].next) {
    				int y=e[i].to;
    				if(vis[y]) cir=gcd(cir, abs(num[x]+e[i].w-num[y])), mx=max(mx, num[y]);
    				else {
    					q[tail++]=y; if(tail==N) tail=0;
    					num[y]=num[x]+e[i].w;
    					vis[y]=1;
    				}
    			}
    		}
    		sum+=mx-mn+1;
    	}
    	if(cir && cir<3) return puts("-1 -1"), 0;
    	if(cir) { int i=3; while(cir%i) ++i; printf("%d %d
    ", cir, i); return 0; }
    	if(sum<3) return puts("-1 -1"), 0;
    	printf("%d 3
    ", sum);
    	return 0;
    }
    

      


    Description

    一年一度的假面舞会又开始了,栋栋也兴致勃勃的参加了今年的舞会。今年的面具都是主办方特别定制的。每个参加舞会的人都可以在入场时选择一 个自己喜欢的面具。每个面具都有一个编号,主办方会把此编号告诉拿该面具的人。为了使舞会更有神秘感,主办方把面具分为k (k≥3)类,并使用特殊的技术将每个面具的编号标在了面具上,只有戴第i 类面具的人才能看到戴第i+1 类面具的人的编号,戴第k 类面具的人能看到戴第1 类面具的人的编号。 参加舞会的人并不知道有多少类面具,但是栋栋对此却特别好奇,他想自己算出有多少类面具,于是他开始在人群中收集信息。 栋栋收集的信息都是戴第几号面具的人看到了第几号面具的编号。如戴第2号面具的人看到了第5 号面具的编号。栋栋自己也会看到一些编号,他也会根据自己的面具编号把信息补充进去。由于并不是每个人都能记住自己所看到的全部编号,因此,栋栋收集的信 息不能保证其完整性。现在请你计算,按照栋栋目前得到的信息,至多和至少有多少类面具。由于主办方已经声明了k≥3,所以你必须将这条信息也考虑进去。

    Input

    第一行包含两个整数n, m,用一个空格分隔,n 表示主办方总共准备了多少个面具,m 表示栋栋收集了多少条信息。接下来m 行,每行为两个用空格分开的整数a, b,表示戴第a 号面具的人看到了第b 号面具的编号。相同的数对a, b 在输入文件中可能出现多次。

    Output

    包含两个数,第一个数为最大可能的面具类数,第二个数为最小可能的面具类数。如果无法将所有的面具分为至少3 类,使得这些信息都满足,则认为栋栋收集的信息有错误,输出两个-1。

    Sample Input

    【输入样例一】

    6 5
    1 2
    2 3
    3 4
    4 1
    3 5

    【输入样例二】

    3 3
    1 2
    2 1
    2 3

    Sample Output

    【输出样例一】
    4 4

    【输出样例二】
    -1 -1

    HINT

    100%的数据,满足n ≤ 100000, m ≤ 1000000。

    Source

  • 相关阅读:
    Zookeeper Acl权限 超级用户权限 怎么跳过ACL密码/账户验证
    白名单与黑名单
    通过mstsc复制粘贴失败需要重新启动RDP剪切板监视程序rdpclip.exe
    go命令帮助
    go build 与go install
    1.Python编程基础
    使用 JMeter 进行压力测试
    js控制手机保持亮屏的库,解决h5移动端,自动息屏问题
    Linux安装配置redis 、启动redis、redis设置密码
    Linux安装部署FTP服务器
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/4113874.html
Copyright © 2020-2023  润新知