zoukankan      html  css  js  c++  java
  • 比特镇旅游(Tourist Attractions)【暴力+Bitset 附Bitset用法】

    Online Judge:NOIP2016十连测第一场 T2

    Label:暴力,Bitset

    题目描述

    在美丽的比特镇一共有n个景区,编号依次为1n,它们之间通过若干条双向道路连接。

    Byteasar 慕名来到了比特镇旅游,不过由于昂贵的门票费,他只能负担起 4 个景区的门票费。他可以在任意景区开始游览,然后结束在任意景区。

    Byteasar 的旅游习惯比较特殊,一旦他路过了一个景区,他就一定会进去参观,并且他永远不会参观同一个景区两次。所以他想知道,有多少种可行的旅游路线,使得他可以恰好参观 4 个景区呢?即,有多少条简单路径恰好经过了 4 个点。

    输入

    第一行包含两个整数n,表示景区的总数。

    2至第n+1行,每行一个长度为n01字符串,第i+1行第j个字符为0表示ij之间没有道路,为1表示有一条道路。

    输入数据保证((i,j))的连接情况等于((j,i))的连接情况,且((i,i))恒为0。

    输出

    输出一行一个整数,即可行的路线总数。

    样例

    Input

    4
    0101
    1010
    0101
    1010
    

    Output

    8
    

    说明/提示

    8 条路线分别为:
    1->2->3->4,4->3->2->1,
    2->3->4->1,1->4->3->2,
    3->4->1->2,2->1->4->3,
    4->1->2->3,3->2->1->4。

    对于40%数据,(n<=50)

    对于70%数据,(n<=300)

    对于100%数据,(n<=1500)

    题解

    40pts

    (O(N^4))枚举四个点。

    70pts

    (O(N^3))

    考虑枚举中间两个点(p2,p3),现在要找到符合条件的对数{(p1,p4)}。需要满足的条件是这四个点两两都不相同。

    (cnt[i])表示与i直接连通的点的个数(就是字符串中含1个数),(same[i][j])表示与i,j都直接相连的点的个数(就是两个字符串对应位都为1的位数)。

    那么上面的符合条件的对数{(p1,p4)}就是((cnt[p2]-1)*(cnt[p3]-1)-same[p2][p3])。都减一是因为(p2,p3)互相相连,新选的(p1,p4)不能跟这两个重了。

    上面的枚举是(O(N^2))的,预处理(same[][])(O(N^3))的。

    100pts

    主要瓶颈在于(same[][])的预处理,其实预处理的时候就马上想到用(Bitset)去优化常数,快速得到这个值,但是考试的时候觉得应该有(N^2)解法,结果Claris的正解就是这东西???

    所以利用(Bitset)二进制存储,计算两者与运算结果中1的个数。

    复杂度为(O(frac{n^3}{64})),对于n<=1500的数据没问题。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1510;
    int n,cnt[N];
    bitset<N>a[N];
    int main(){
    	scanf("%d",&n);
    	register int i,j;char c;
    	for(i=1;i<=n;i++)for(j=1;j<=n;j++){
    		c=getchar();
    		while(c!='0'&&c!='1')c=getchar();
    		a[i][j]=c-'0';
    		cnt[i]+=c-'0';
    	}
    	long long ans=0;
    	for(i=1;i<=n;i++)for(j=i+1;j<=n;j++)if(a[i][j]==1){
    		ans+=1ll*(cnt[i]-1)*(cnt[j]-1)-1ll*(a[i]&a[j]).count();
    	}
    	printf("%lld
    ",ans<<1);
    }
    

    END

    整理一下(Bitset)的用法。参考博客->

    定义/初始化

    <>中填的类似数组大小,后面可以开成单个变量,也可以开成一个数组。

    bitset<LEN>a;
    bitset<2333>b[N];
    

    初始化大概有如下几种方式。

     #include<bits/stdc++.h>
     using namespace std;
     int main(){
    	freopen("test.out","w",stdout);
    	//用string(似乎只能包含01,不然运行时会报错)
    	bitset<10>a (string("11011"));
    	cout<<"#1:"<<a<<endl;
    	
    	//用整数(转为二进制)
    	a=123;
    	cout<<"#2:"<<a<<endl;
    
    	//直接像数组那样赋值(下标从0开始,长度为10)
    	for(int i=0;i<=9;i++){
        	int o;scanf("%d",&o);//测试时输入1 0 1 0 0 0 1 1 0 1
        	a[i]=o;
    	}
    	cout<<"#3:"<<a<<endl;
    }
    

    输出结果

    #1:0000011011
    #2:0001111011
    #3:1011000101
    

    运算操作

    (Bitset)支持所有位运算。包括异或^、或|、与&、左移<<、右移>>等。

    常用函数

    查询类

    1.返回1的个数

    a.count();
    

    2.返回是否有1

    a.any();
    

    操作类

    3.全部变成1

    a.set();
    

    4.将右数(i+1)变成1(实际上就是将二进制的第i位变成1,下标从0开始)

    a.set(i);
    //例: a.set(2) 0000000100
    

    5.全部变为0(()中间填数字时指定某一位,同上)

    a.reset();
    

    6.取反(()中间填数字时指定某一位,同上)

    a.flip();
    

    至于常数优化是多少,好像跟机器位数有关(32/64)。

  • 相关阅读:
    那些离不开的 Chrome 扩展插件
    Spring Boot 实战 —— 入门
    Maven 学习笔记
    Linux lvm 分区知识笔记
    Linux 双向 SSH 免密登录
    CentOS Yum 源搭建
    Ubuntu 系统学习
    iOS 测试三方 KIF 的那些事
    Swift 网络请求数据与解析
    iOS Plist 文件的 增 删 改
  • 原文地址:https://www.cnblogs.com/Tieechal/p/11545130.html
Copyright © 2011-2022 走看看