4467奇妙的方式优化暴力的01边查询
生活随笔
收集整理的這篇文章主要介紹了
4467奇妙的方式优化暴力的01边查询
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題意:
? ? ? 給你n個點,每個點上只有兩種顏色0,1,然后m條邊每條邊上都有權值,然后兩種操作,第一種是詢問,給你ab,(ab=00 ,11,01/10),然后問你這樣的邊的權值和是多少?第二種操作就是把一個點的顏色取反。
思路:
? ? ? 用了將近一天去理解這個,看到網上的題解幾乎都一樣,感覺沒說到重點怎么看也不會,最后不小心看到有哥們的博客,然后模擬幾遍,有了自己的理解,首先這個題目暴力的話時間復雜度最壞是O(q*m)的,10W*10W比較大會TLE,然后就是去優化暴力的姿勢,單純暴力的話很容易想到兩種方法,一個是每次更新時單純的計算邊的權值然后進行更新,另一個就是給每個點都開個變量,紀錄連接他的0的總權值和連接他的1的總權值,去更新,兩個時間復雜度是一樣的,下面說下AC的方法,沒記錯應該是兩種做法吧,思想大體相同,想想上面的兩種暴力方法是不是要建立雙向邊?也就是沒有收到邊的方向限制,如果加上邊的方向限制怎么樣保證正確性?保證正確性之后怎么樣保證他的速度是快的?一一解釋:
deg[i] 表示i節點的度數
OI[a][b]表示和a節點相連的b顏色點的權值和
首先我們要把每個點的度數求出來,然后如果deg[a]<=deg[b],那么建立邊a->b,然后把b的相鄰的a的顏色加上b的權值,也就是OI[b][Color[a]] += cost,然后每次更新的時候就是處理兩個問題,一個是邊相連的,這個直接暴力,然后就是計算OI[][],這個可以o1時間搞定,還有就是更新完之后在吧相鄰的OI[][]更新了,查詢是o1的時間復雜度,這樣就完事了,這么說完肯定沒聽懂!
思想就是把無向邊變有向,向下是暴力更新,向上是計算01的總和,時間復雜度據說是O(q*sqrt(m)) 這個用我的這個方法的話我證明不清楚,網上的那個sqrt(m)為界限的好證明,我這么實現我覺得不好證明,但是我感覺這么寫會比sqrt的那個快(我自己感覺),我只能說這個方法肯定是優化的,而且可以AC。
(1)為什么建邊的時候是小的度數指向大的度數,可以反過來嗎?
?
:小指向大是為了把暴力的部分給邊少的點來跑,反過來后會超時,這個要是能理解建邊的理由的話應該很容易想到。
?
(2)怎么保證正確性?
:這個是我想了一天的地方,首先,把無向邊變成有向邊后會怎樣?是不是通過邊查找更新的時候只能更新下面的,也就是自己指向的,而沒有更新指向自己的,指向自己的我們可以用另一種暴力的方法,就是記錄每個點相連的0顏色和1顏色的總權值,然后可以直接算出來,每次更新的時候向上直接算,向下暴力跑,然后向下暴力跑的時候記得把下面那個點的上面更新了,這個地方非常關鍵,如果能理解了這就應該知道這么做的正確性以及速度快的原因,但是時間復雜度算起來...
(3)注意度數相等的時候的建邊方式,可以隨意指向方向,但是記住盡量別建雙向邊,雙向邊在更新的時候還要特出處理,麻煩。
(4)還有什么坑點
:a數據范圍 longlong或者__ing64
:b重復邊問題,記得合并重復邊,不然同樣的邊不停出現優化會沒有意義,這個要是能明白(2)應該很容易想到。
好大體就這樣,這個題主要是靠自己理解,要去想這樣做為什么是快的,這樣做的正確性是怎么保證的,我覺得這道題必須要明白這兩點,不然做了相當于沒做。 ? ??
? ?
? ? ? 給你n個點,每個點上只有兩種顏色0,1,然后m條邊每條邊上都有權值,然后兩種操作,第一種是詢問,給你ab,(ab=00 ,11,01/10),然后問你這樣的邊的權值和是多少?第二種操作就是把一個點的顏色取反。
思路:
? ? ? 用了將近一天去理解這個,看到網上的題解幾乎都一樣,感覺沒說到重點怎么看也不會,最后不小心看到有哥們的博客,然后模擬幾遍,有了自己的理解,首先這個題目暴力的話時間復雜度最壞是O(q*m)的,10W*10W比較大會TLE,然后就是去優化暴力的姿勢,單純暴力的話很容易想到兩種方法,一個是每次更新時單純的計算邊的權值然后進行更新,另一個就是給每個點都開個變量,紀錄連接他的0的總權值和連接他的1的總權值,去更新,兩個時間復雜度是一樣的,下面說下AC的方法,沒記錯應該是兩種做法吧,思想大體相同,想想上面的兩種暴力方法是不是要建立雙向邊?也就是沒有收到邊的方向限制,如果加上邊的方向限制怎么樣保證正確性?保證正確性之后怎么樣保證他的速度是快的?一一解釋:
deg[i] 表示i節點的度數
OI[a][b]表示和a節點相連的b顏色點的權值和
首先我們要把每個點的度數求出來,然后如果deg[a]<=deg[b],那么建立邊a->b,然后把b的相鄰的a的顏色加上b的權值,也就是OI[b][Color[a]] += cost,然后每次更新的時候就是處理兩個問題,一個是邊相連的,這個直接暴力,然后就是計算OI[][],這個可以o1時間搞定,還有就是更新完之后在吧相鄰的OI[][]更新了,查詢是o1的時間復雜度,這樣就完事了,這么說完肯定沒聽懂!
思想就是把無向邊變有向,向下是暴力更新,向上是計算01的總和,時間復雜度據說是O(q*sqrt(m)) 這個用我的這個方法的話我證明不清楚,網上的那個sqrt(m)為界限的好證明,我這么實現我覺得不好證明,但是我感覺這么寫會比sqrt的那個快(我自己感覺),我只能說這個方法肯定是優化的,而且可以AC。
(1)為什么建邊的時候是小的度數指向大的度數,可以反過來嗎?
?
:小指向大是為了把暴力的部分給邊少的點來跑,反過來后會超時,這個要是能理解建邊的理由的話應該很容易想到。
?
(2)怎么保證正確性?
:這個是我想了一天的地方,首先,把無向邊變成有向邊后會怎樣?是不是通過邊查找更新的時候只能更新下面的,也就是自己指向的,而沒有更新指向自己的,指向自己的我們可以用另一種暴力的方法,就是記錄每個點相連的0顏色和1顏色的總權值,然后可以直接算出來,每次更新的時候向上直接算,向下暴力跑,然后向下暴力跑的時候記得把下面那個點的上面更新了,這個地方非常關鍵,如果能理解了這就應該知道這么做的正確性以及速度快的原因,但是時間復雜度算起來...
(3)注意度數相等的時候的建邊方式,可以隨意指向方向,但是記住盡量別建雙向邊,雙向邊在更新的時候還要特出處理,麻煩。
(4)還有什么坑點
:a數據范圍 longlong或者__ing64
:b重復邊問題,記得合并重復邊,不然同樣的邊不停出現優化會沒有意義,這個要是能明白(2)應該很容易想到。
好大體就這樣,這個題主要是靠自己理解,要去想這樣做為什么是快的,這樣做的正確性是怎么保證的,我覺得這道題必須要明白這兩點,不然做了相當于沒做。 ? ??
#include<map> #include<stdio.h> #include<string.h>#define N_node 100000 + 10 #define N_edge 200000 + 20using namespace std;typedef struct {int to ,next;long long cost; }STAR;typedef struct {int a ,b;long long c; }EDGE;STAR E[N_edge]; EDGE edge[N_edge]; map<long long ,long long>mark; int list[N_node] ,tot; int deg[N_node]; int Color[N_node]; long long OI[N_node][2];void add(int a, int b ,long long c) {E[++tot].to = b;E[tot].cost = c;E[tot].next = list[a];list[a] = tot; }int main () {int n ,m ,q ,a ,b ,cas = 1 ,i;long long c ,O[5];char str[10];while(~scanf("%d %d" ,&n ,&m)){for(i = 1 ;i <= n ;i ++)scanf("%d" ,&Color[i]);memset(deg ,0 ,sizeof(deg));mark.clear();int nowid = 0;O[0] = O[1] = O[2] = 0;for(i = 1 ;i <= m ;i ++){scanf("%d %d %lld" ,&a ,&b ,&c);O[Color[a]+Color[b]] += c;if(a > b) a =a + b ,b = a - b ,a = a - b;if(!mark[a*(long long)1000050 + b]){mark[a*(long long)1000050 + b] = ++nowid;edge[nowid].a = a ,edge[nowid].b = b;edge[nowid].c = c;deg[a] ++ ,deg[b] ++;}else edge[mark[a*(long long)1000050+b]].c += c;}memset(list ,0 ,sizeof(list)) ,tot = 1;memset(OI ,0 ,sizeof(OI));for(i = 1 ;i <= nowid ;i ++){a = edge[i].a ,b = edge[i].b ,c = edge[i].c;if(deg[a] < deg[b]) add(a ,b ,c) ,OI[b][Color[a]] += c;else add(b ,a ,c) ,OI[a][Color[b]] += c;}printf("Case %d:\n" ,cas ++);scanf("%d" ,&q);while(q--){scanf("%s" ,str);if(str[0] == 'A'){scanf("%d %d" ,&a ,&b);printf("%lld\n" ,O[a+b]);}else{scanf("%d" ,&a);O[Color[a]+0] -= OI[a][0];O[(Color[a]^1)+0] += OI[a][0];O[Color[a]+1] -= OI[a][1];O[(Color[a]^1)+1] += OI[a][1];for(int k = list[a] ;k ;k = E[k].next){b = E[k].to;O[Color[a]+Color[b]] -= E[k].cost;O[(Color[a]^1)+Color[b]] += E[k].cost;OI[b][Color[a]] -= E[k].cost;OI[b][Color[a]^1] += E[k].cost;}Color[a] ^= 1;}}}return 0; }
? ?
總結
以上是生活随笔為你收集整理的4467奇妙的方式优化暴力的01边查询的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: POJ1364基本的查分约束问题
- 下一篇: cf534D 枚举握手次数