1. 程式人生 > >HDU 6350 2018HDU多校賽 第五場 Always Online(圖論 + 並查集 + 組合計數)

HDU 6350 2018HDU多校賽 第五場 Always Online(圖論 + 並查集 + 組合計數)

         

大致題意:給你一個仙人掌圖,讓你計算:\large \sum_{1\leq i< j\leq n}i\oplus j \oplus flow(i,j)

根據去年多校賽某一道題的經驗,很多仙人掌圖的問題,其實可以轉化為樹的問題。所以我們同樣考慮,如果這是一棵樹的話如何去做。注意到表示式裡面的flow(i,j)表示從i到j的最小割或最大流,而在樹上的最小割可以看作是兩點之間連線的最短邊,那麼我們要做的就是統計每一條邊作為最短邊的貢獻。這樣我們不禁就聯想到了之前做過的 Codeforces 915F 。這題是求任意兩點之間路徑上最長邊減去最短邊之差之和。

和那題類似,在不考慮仙人掌圖,而是普通樹的情況下,本題考慮對邊進行排序,然後不斷的並查集合並統計每條邊產生的新貢獻。那麼我們現在考慮,當不是樹的時候,根據仙人掌圖的定義,每個點最多隻能在一個簡單環內部。我們考慮包含這樣一個環的路徑的最小割,這樣的最小割,要麼是普通路徑上的一條邊,要麼是環中的最短邊加上另一條邊。具體可以參見下圖:

   

我們要麼切掉類似紅色的邊,要麼切掉兩條類似兩條綠色的邊,然後可以證明兩個綠色的邊一定會有一條環中流量最小的邊。既然如此,如果我們找到這條邊,然後把這個權值加入到環中的其他邊中,然後刪掉這條邊,再求最小割對最後結果也不會產生影響。所以我們不妨把所有的環都這麼處理,這樣一個仙人掌圖就會變成一棵樹,我們也就可以按照之前所說的那麼做了。

具體來說,如果找到這個最小的邊呢。我們不妨做一遍最大生成樹,這樣剩下的邊就一定能夠與某些點構成環,而且一定是這些環中的邊裡面流量最小的。然後我們dfs求出fa和dep等資訊,然後遍歷環中的邊,把每條邊的權值加上這個最小流量。之後就用這個新的樹新的邊求解。本題需要考慮的是路徑中的最小值,所以我們邊也是一樣從最大的開始合併。每次並查集合並,然後計算貢獻。

這個貢獻,由於又要考慮上兩個端點i和j,所以還要有一些技巧。具體來說於 Codeforces 724G 有點類似。分別統計每一個連通塊裡面有多少個數在對應二進位制位的值是1和0的個數。合併的時候一起合併即可。還有,最後答案比較坑,需要用到ull。具體見程式碼:

#include<bits/stdc++.h>
#define N 200010
#define LL long long
#define IO ios::sync_with_stdio(0);cin.tie(0)
using namespace std;

struct edge
{
    int x,y,w;

    bool operator < (const edge a) const
    {
        return w>a.w;
    }

} e[N];

int n,m,f[N],fa[N],dep[N],pre[N];
int cnt[N][32][2],sz[N][32][2];
struct Edge{int y,w;};
vector<Edge> g[N];
vector<edge> G;

int find(int x)
{
    return f[x]==x?x:f[x]=find(f[x]);
}

void dfs(int x,int ff,int d)
{
    fa[x]=ff; dep[x]=d;
    for(int i=0;i<g[x].size();i++)
    {
        int y=g[x][i].y;
        if (y==ff) continue;
        dfs(y,x,d+1); pre[y]=i;
    }
}

void init()
{
    for(int i=1;i<N;i++)
        for(int j=0;j<31;j++)
        {
            cnt[i][j][0]=(i>>j)&1^1;
            cnt[i][j][1]=(i>>j)&1;
        }
}

int main()
{
    IO; int T;
    cin>>T; init();
    while(T--)
    {
        cin>>n>>m;
        G.clear();
        for(int i=1;i<=n;i++)
        {
            f[i]=i,g[i].clear();
            for(int j=0;j<31;j++)
            {
                sz[i][j][0]=cnt[i][j][0];
                sz[i][j][1]=cnt[i][j][1];
            }
        }
        for(int i=1;i<=m;i++)
            cin>>e[i].x>>e[i].y>>e[i].w;
        sort(e+1,e+1+m);
                                                    //kruskal
        for(int i=1;i<=m;i++)
        {
            int x=e[i].x,y=e[i].y,w=e[i].w;
            if (find(x)!=find(y))
            {
                f[find(x)]=find(y);
                g[x].push_back(Edge{y,w});
                g[y].push_back(Edge{x,w});
            } else G.push_back(edge{x,y,w});
        }
                                                    //get fa[] & pre[] & dep[]
        dfs(1,0,0);
                                                    //delete the minimum edge in the circle
        for(int i=0;i<G.size();i++)
        {
            int x=G[i].x,y=G[i].y,w=G[i].w;
            if (dep[x]<dep[y]) swap(x,y);
            for(;dep[x]>dep[y];x=fa[x])
                g[fa[x]][pre[x]].w+=w;
            for(;x!=y;x=fa[x],y=fa[y])
            {
                g[fa[x]][pre[x]].w+=w;
                g[fa[y]][pre[y]].w+=w;
            }
        }
                                                    //rebuild the graph and sort
        int tot=0;
        for(int i=1;i<=n;i++)
            for(int j=0;j<g[i].size();j++)
                if (dep[g[i][j].y]>dep[i])
                    e[++tot]=edge{i,g[i][j].y,g[i][j].w};
        sort(e+1,e+1+tot);
                                                    //initial dsu
        for(int i=1;i<=n;i++) f[i]=i;
                                                    //calculate the answer
        unsigned long long ans=0;
        for(int i=1;i<=tot;i++)
        {
            int x=e[i].x,y=e[i].y,w=e[i].w;
            x=find(x); y=find(y); f[x]=y;
            for(int j=0;j<31;j++)
            {
                if ((w&(1LL<<j))) ans+=(1LL*sz[x][j][0]*sz[y][j][0]+1LL*sz[x][j][1]*sz[y][j][1])*(1LL<<j);
                             else ans+=(1LL*sz[x][j][0]*sz[y][j][1]+1LL*sz[x][j][1]*sz[y][j][0])*(1LL<<j);
                sz[y][j][0]+=sz[x][j][0]; sz[y][j][1]+=sz[x][j][1];
            }
        }

        cout<<ans<<endl;
    }
    return 0;
}

相關推薦

HDU 6350 2018HDU Always Online + + 組合計數

          大致題意:給你一個仙人掌圖,讓你計算:。 根據去年多校賽某一道題的經驗,很多仙人掌圖的問題,其實可以轉化為樹的問題。所以我們同樣考慮,如果這是一棵樹的話如何去做。注意到表示式裡面的flow(i,j)表示從i到j的最小割或最大流,而在樹上的最小

HDU 6327 2018HDU Random Sequence概率期望dp+數論

Problem I. Random Sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others) Total Submissio

HDU 6340 2018HDU Delightful Formulas莫比烏斯反演+伯努利數+NTT+積性

大致題意:給你k和m,還有n分解質因子之後的質因子及其對應的指數,讓你求 。 首先,這種含有gcd的式子,第一步肯定是進行莫比烏斯反演,這裡由於前面好幾篇都由類似的反演形式,所以我就不展開了,直接就得出反演之後的結果:              

HDU 6338 2018HDU Depth-First Search組合數學+平衡樹/pbds

大致題意:給你一個dfs序列B和一棵樹,現在讓你在這個樹上隨機選擇一個點,然後按照隨機的dfs順序走。問你最後能走出幾個dfs序列,是得該dfs序列字典序小於給定的dfs序B。 首先,我們考慮一棵樹有根樹他的dfs序有多少種。我們可以這麼考慮,對於任意點x,

HDU 6428 2018HDU Calculate莫比烏斯反演 + 積性 + 線性篩

題意簡單粗暴,讓你求  。 與gcd有關,一般來說都是要上莫比烏斯來反演一下了。具體來說,我們先來推一些式子:                                      

HDU 6416 2018HDU Rikka with Seamdp + 字首和優化

大致題意:給你一個n*m的01矩陣,現在要讓你每一行和每一列都去掉一個數字,而且要求相鄰兩行之間去掉數字的位置的絕對值要小於等於k。現在問你刪除之後的矩形最多有幾種。 首先,我們一行一行考慮,對於同一行,顯然是看有多少個塊,有多少個塊就有多少個方案。然後對於

HDU9 HDU 4965Fast Matrix Calculation【矩陣運算+數學小知識】

stdin amp line you stream [] nbsp content ans 難度上。,,確實。。。不算難 問題是有個矩陣運算的優化 題目是說給個N*K的矩陣A給個K*N的矩陣B(1<=N<=1000 && 1=<K<

HDU 6314 2018HDU第二 Matrix容斥原理+組合計數

Matrix Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 332768/332768 K (Java/Others) Total Submission(s): 445    Accepted Submiss

2017年 1005 FFF at Valentine縮點+拓撲排序

其實縮點很容易想到,就是不怎麼會用拓撲排序所以一直卡著判斷這個地方。 程式碼如下: #include <stdio.h> #include <string.h> #include <vector> #include <s

2017年 1006 Senior Pandijkstra套路題

這道題有很多解法,我是按照題解的做法寫了一遍。 題意:給出一個有向圖,然後有k個點,問這k個點兩兩之間的最短距離的最小值是多少。 解法:題解是把k個點按照每個點二進位制位的不同分成兩個集合,然後求一次多源最短路。由於最大的數是1e5,所以也就到個。。。2^17的樣子?由於

2014聯合-

1001:Inversion 模版題,求逆序數對。有多少逆序數對,就可以剪掉多少。 1003:Least common multiple 對於每一個子集,lcm為2的a的最大值次方*3的b的最大值次方。 所以我們只需要求出以某個b為b的最大值的時候,a的最大值的分佈情況即可

hdu6434 杭電 1009 I題 count

Problem I. Count Problem Description Multiple query, for each n, you need to get n i-1 ∑ ∑ [gcd(i + j, i - j) = 1] i=1 j=1 Input On th

牛客網訓練營gpa01分數規劃

01分數規劃 01分數規劃問題:所謂的01分數規劃問題就是指這樣的一類問題,給定兩個陣列,a[i]表示選取i的收益,b[i]表示選取i的代價。如果選取i,定義x[i]=1否則x[i]=0。每一個物品只有選或者不選兩種方案,求一個選擇方案使得R=sigma(a[i]*x[

2018-2019賽季聯合新生訓練(2018/12/14)補題題解

A 【字串】ISBN號碼(字串基礎) 字串基礎題,注意一下x的特判即可 程式碼 #include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(0); cin.tie(0); cout

HDU 5399 Too Simple 2015年比賽9

assert vector math php post long long size 答案 ring 1.題目描寫敘述:點擊打開鏈接 2.解題思路:本題分情況討論。比賽時候真是想的太簡單了。以為就是(n!)^(cnt-1)。終於無限WA。本題有幾個特殊情況須要額外推斷。

HDU 5411 CRB and Puzzle 2015年比賽10

理解 tor for truct rac iostream blank 全部 sta 1.題目描寫敘述:pid=5411">點擊打開鏈接 2.解題思路:本題實際是是已知一張無向圖。問長度小於等於m的路徑一共同擁有多少條。能夠通過建立轉移矩陣利用矩陣高速冪解決。當中

2017聯合91010 Two String/hdu 6170正則表示式/dp

Two strings Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 680    Accepted Su

2017聯合HDU 6171 Admiral 雙向bfs + 雜湊

Suppose that you are an admiral of a famous naval troop. Our naval forces have got 21 battleships. There are 6 types of battleships. First, we have got on

2017訓練第一 HDU 6039 Gear Up線段樹+

Gear Up Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 59    Accepted Submiss

hdu 4911 Inversion歸併排序求逆序對數2014訓練5

Inversion                                                                             Time Limit: 2000/1000 MS (Java/Others)    Memory L