acwing-239-奇偶游戏(离散化+前缀和+带权并查集)+acwing164可达性统计(bitset使用+拓扑排序)

acwing-239-奇偶游戏(离散化+前缀和+带权并查集)

题意:

小A和小B在玩一个游戏。

首先,小A写了一个由0和1组成的序列S,长度为N。

然后,小B向小A提出了M个问题。

在每个问题中,小B指定两个数 l 和 r,小A回答 S[l~r] 中有奇数个1还是偶数个1。

机智的小B发现小A有可能在撒谎。

例如,小A曾经回答过 S[1~3] 中有奇数个1, S[4~6] 中有偶数个1,现在又回答 S[1~6] 中有偶数个1,显然这是自相矛盾的。

请你帮助小B检查这M个答案,并指出在至少多少个回答之后可以确定小A一定在撒谎。

即求出一个最小的k,使得01序列S满足第1~k个回答,但不满足第1~k+1个回答。

 

解:

1,n的范围在1e9,但是m的范围在1e4,m次输入每次最多两个数,所以数据大概在2e4,需要离散化一下,将不同数据映射进去;

2,使用前缀和 和 并查集处理问题

计算l和r范围内1的数有奇数个还是偶数个,即为计算sum【r】-sum[l-1];,具体的题中不用进行计算...

并查集即为前缀和中每个元素的奇偶性(奇数-奇数=偶数,偶数-偶数=偶数,奇偶性不同时,会出现差值为奇数的情况)

所有前缀和元素会合并到一个祖先之中,d[x]维护x与根节点奇偶性是否相同

 如果两个元素已经合并在同一个祖先下,那么就可以根据他们与祖先的奇偶性异同,得到他们的异同,判断他们与输入的异同是否一致 如果不一致就是发生矛盾,输出答案...

%2!=&1

异或是相同为0,不同为1,奇数末尾&1=0,偶数末尾&1=1;

奇数%2=1,偶数%2=0...

代码:

#include<bits/stdc++.h>
const int maxn=1e6+10;
typedef long long ll;
using namespace std;

ll n;
int m;

int fa[maxn],d[maxn];
int cnt=0;
unordered_map<int ,int> mp;

int get(int x)//离散化
{
    if(mp.count(x)==0) mp[x]=++cnt;
    return mp[x];
}

void init()
{
    for(int i=1; i<=maxn-10; i++) 
     fa[i]=i;
}

int find(int x)
{
    if(x!=fa[x])
    {
        int root=find(fa[x]);
        d[x]+=d[fa[x]]%2;//判断奇偶
        fa[x]=root;
    }
    return fa[x];
}

int main()
{
    cin>>n>>m;
    int ans=m;
    init();
    for(int i=1; i<=m; i++)
    {
        int x,y;
        string  s;
        cin>>x>>y>>s;
        x=get(x-1),y=get(y);
        int fx=find(x),fy=find(y);
        if(fx==fy)
        {
            if(s=="even")
            {
                //奇偶性相同
                if((d[x]+d[y])%2!=0)//奇数,有矛盾
                {
                    ans=i-1;
                    break;
                }
            }
            else if(s=="odd")
            {//奇偶性不同
                if((d[x]+d[y])%2!=1)//偶数,有矛盾
                {
                    ans=i-1;
                    break;
                }
            }
        }
        else
        {//合并
            fa[fx]=fy;//0,1区分奇偶
            int add=0;
            if(s=="odd") add=1;
            d[fx]=(d[x]+d[y]+add)%2;
        }
        
    }
    cout<<ans<<endl;
    system("pause");
    return 0;
}

 acwing164可达性统计(bitset使用+拓扑排序)

题意:统计一个点能到多少个点,输出

解:

首先用拓扑排序对点进行排序,再使用bitset进行位运算压缩,求并集。

bitset的使用:

类似于bool,但是它的每个位置只占1bit(特别小)

bitset的原理大概是将很多数压成一个,从而节省空间和时间

一般来说bitset会让你的算法复杂度 /32

定义bitset<maxn> bit;

bitset支持所有的位运算;

对于一个叫做bit的bitset:
bit.size() 返回大小(位数)
bit.count() 返回1的个数
bit.any() 返回是否有1
bit.none() 返回是否没有1
bit.set() 全都变成1
bit.set(p) 将第p + 1位变成1(bitset是从第0位开始的!)
bit.set(p, x) 将第p + 1位变成x
bit.reset() 全都变成0
bit.reset(p) 将第p + 1位变成0
bit.flip() 全都取反
bit.flip(p) 将第p + 1位取反
bit.to_ulong() 返回它转换为unsigned long的结果,如果超出范围则报错
bit.to_ullong() 返回它转换为unsigned long long的结果,如果超出范围则报错
bit.to_string() 返回它转换为string的结果

 

此题用到bit.count(),bit.reset(),当然此题的bit是一个数组;

代码:

#include<bits/stdc++.h>
const int maxn=3e4+10;
typedef long long ll;
using namespace std;

vector<int> G[maxn];
vector<int> Tup;
bitset<maxn> F[maxn];
int in[maxn];
int n, m;

void Tupo()
{
    queue<int> que;
    for (int i = 1;i <= n;i++)
    {
        if (in[i] == 0)
            que.push(i);
    }
    while (!que.empty())
    {
        int node = que.front();
        que.pop();
        Tup.push_back(node);
        for (int i = 0;i < G[node].size();i++)
        {
            int to = G[node][i];
            if (--in[to] == 0)
                que.push(to);
        }
    }
}


void Solve()
{
    for (int i = n-1;i >= 0;i--)
    {
        int x = Tup[i];
        F[x].reset();
        F[x][x] = 1;
        for (int k = 0;k < G[x].size();k++)
        {
            F[x] |= F[G[x][k]];
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    int u, v;
    for (int i = 1;i <= m;i++)
    {
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        in[v]++;
    }
    Tupo();
    Solve();
    for (int i = 1;i <= n;i++)
        printf("%d\n", (int)F[i].count());

    return 0;
}

 

上一篇:【小米OJ-海盗分赃】c++中bitset的使用


下一篇:有关bitset的用法 (转载)