1. 程式人生 > 實用技巧 >acwing-239-奇偶遊戲(離散化+字首和+帶權並查集)+acwing164可達性統計(bitset使用+拓撲排序)

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;
}