2018/8/9 MultiU 6 并查集+dfs,反向建边提高查询效率 !!! / 最大字段和n维(降维)/ 状压+中途相遇法...
hdu6370 Werewolf
?
http://acm.hdu.edu.cn/showproblem.php?pid=6370
題意:村民只能說真話,狼人“可以”撒謊,每個人說一句話指向出自己之外任意一人身份,問與多少鐵民和鐵狼。
思路:1.因為可以有全狼的情況,所以不存在鐵民。
? ? ? ? ? ?2.通過基環(huán)樹找狼。(具體參見下圖,搬運于別人的博客QAQ。。。)
?
? ? 當我們發(fā)現(xiàn)有連續(xù)個點是有村民的邊,如點1,點2,點3,點4,點5,點6;而這些個連續(xù)的其中一個點(如圖中的點6)有一條狼人邊連到了這些個連續(xù)的其他的點(如上圖連到了點1)。
? ? 此時,我們用反證法可以證明,倘若1號點是村民,則根據(jù)村民不會說謊的性質(zhì)可以判斷出1到6號點全是村民,而根據(jù)村民不會說謊的性質(zhì),只能證明出1號點必為狼人。此時我們同時也可以發(fā)現(xiàn),倘若1號點是狼人,則根據(jù)狼人會說謊的性質(zhì)可知,指向1號點為村民的也必定是狼人。
? ? 因此我們的算法雛形就初步顯現(xiàn)出來了。
? ? 我們要維護一些連續(xù)的村民點,可以用一個并查集進行維護。我們可以將村民邊上的兩個點不斷的用并查集去合并,而當我們遍歷狼邊的時候,倘若我們發(fā)現(xiàn)狼邊上的兩個點都在一個集合中,則說明必定滿足上述的情況,則我們不斷遍歷這條狼人邊所指向的那個結(jié)點(如上圖的1號點),判斷有多少條指向它的村民邊即可。(此處我們可以將村民邊反向建立,這樣可以讓我們高效的查詢)。
參考題解:https://blog.csdn.net/weixin_39453270/article/details/81515570
?
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 1e5+5; 5 typedef pair<int, int> pii; 6 vector<int> vi[N]; 7 vector<pii>wolf; 8 int p[N],n; 9 10 void init(){ 11 for(int i=0;i<=n;i++) p[i]=i, vi[i].clear(); 12 wolf.clear(); 13 } 14 int root(int x){ 15 while(p[x]!=x) x=p[x]; 16 return x; 17 } 18 bool same(int x, int y){ 19 return root(x) == root(y); 20 } 21 void unit(int x, int y){ 22 int tmp1 = root(x), tmp2 = root(y), cur; 23 if(tmp1 == tmp2) return; 24 p[tmp2] = tmp1; 25 while(y != tmp1){ 26 cur = p[y]; 27 p[y] = tmp1; 28 y = cur; 29 } 30 } 31 int ans; 32 void dfs(int x){ 33 int sz = vi[x].size(); 34 for(int i = 0; i < sz; i++){ 35 ans++; 36 dfs(vi[x][i]); 37 } 38 } 39 40 int main() 41 { 42 int t, x; 43 char s[20]; 44 scanf("%d", &t); 45 while(t--){ 46 scanf("%d", &n); 47 init(); 48 for(int i = 1; i <= n; i++){ 49 scanf("%d %s", &x, s); 50 if(s[0] == 'w'){ 51 wolf.push_back(make_pair(i, x));///inverse. 52 } 53 else{ 54 vi[x].push_back(i); 55 unit(x, i); 56 } 57 } 58 ans = 0; 59 int sz = wolf.size(); 60 for(int i = 0; i < sz; i++){ 61 if(same(wolf[i].first, wolf[i].second)){ 62 ans++; 63 dfs(wolf[i].second); 64 } 65 } 66 printf("0 %d\n", ans); 67 } 68 return 0; 69 } View Code?打多校的幾場下來,就是a完水題就掛機,然后賽后看題解,有的時候看完題解還是不知道該怎么寫,于是看別人的代碼,于是于是。。。依然啥也不會。。。(怨念。。o(一︿一+)o)
?
頹廢度日,看底特律到睡著后決定補大白上的題然后。。。QAQ。。。lrj太強了!!!伏地膜!!!啊啊啊啊啊人家寫出來的程序怎么這么xxxxxxx!!!x~
uva10755?G - Garbage Heap
題意:求解最大子“立方體”和。
思路:三位最大子段和,降維->二維,暴力枚舉子矩陣O(n^4),第三維度采取最大子段和O(n)的dp解決,總復(fù)雜度O(n^5)。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long ll; 5 const int N = 20 + 5; 6 const ll inf = 1ll<<60;/// 7 ll S[N][N][N]; 8 int a, b, c, b0, b1, b2; 9 10 void expand(int i, int& b0, int& b1, int& b2){ 11 b0 = i&1; i >>= 1; 12 b1 = i&1; i >>= 1; 13 b2 = i&1; 14 } 15 int sign(int b0, int b1, int b2){ return (b0+b1+b2)&1? 1: -1;} 16 17 ll sum(int x1, int x2, int y1, int y2, int z1, int z2){ 18 int dx = x2-x1+1, dy = y2-y1+1, dz = z2-z1+1; 19 ll s = 0; 20 for(int i = 0; i < 8; i++){ 21 expand(i, b0, b1, b2); 22 s -= S[x2-b0*dx][y2-b1*dy][z2-b2*dz] * sign(b0, b1, b2); 23 } 24 return s; 25 } 26 27 int main() 28 { 29 int t; 30 scanf("%d", &t); 31 while(t--){ 32 scanf("%d %d %d", &a, &b, &c); 33 for(int i = 1; i <= a; i++) for(int j = 1; j <= b; j++) for(int k = 1; k <= c; k++) scanf("%lld", &S[i][j][k]); 34 for(int x = 1; x <= a; x++){ 35 for(int y = 1; y <= b; y++){ 36 for(int z = 1; z <= c; z++){ 37 for(int i = 1; i <= 7; i++){ 38 expand(i, b0, b1, b2); 39 S[x][y][z] += S[x-b0][y-b1][z-b2] * sign(b0,b1,b2); 40 } 41 } 42 } 43 } 44 ll ans = -inf, s;///... 45 for(int x1 = 1; x1 <= a; x1++){ 46 for(int x2 = x1; x2 <= a; x2++){ 47 for(int y1 = 1; y1 <= b; y1++){ 48 for(int y2 = y1; y2 <= b; y2++){ 49 ll M = 0; 50 for(int z = 1; z <= c; z++){ 51 s = sum(x1, x2, y1, y2, 1, z); 52 ans = max(ans, s - M); 53 M = min(M, s);///M<=0~~~ 54 } 55 } 56 } 57 } 58 } 59 printf("%lld\n", ans); 60 if(t) puts("");///PE... 61 } 62 return 0; 63 } View Code學(xué)習(xí)了lrj 的程序,思路清晰,解法實用有效,很容易將問題推廣到四維或更高維度的情形。
?
H - Jurassic Remains(UVALive - 2965)
題意:n(<=24? ? 24/2=12可狀壓)個字符串,選盡量多個串使得每個大寫字母(26個字母, 可狀壓)都出現(xiàn)偶數(shù)次。
思路:1.將每個串轉(zhuǎn)化為二進制,出現(xiàn)偶數(shù)次即為該位xor后為0。
? ? ? ? ? ?2.將字符串集合劃分為兩個集合,也轉(zhuǎn)化為二進制,則有AxorB=0 -> A = B。(A、B為兩集合中所有二進制異或和)。
? ? ? ? ? ?3.對于每個A=B,找到包含字符串數(shù)目最多(二進制中1最多->bitcount最大)的集合,這些集合中bitcount最大的集合即為答案~
?
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 24; 5 int A[N]; 6 map<int, int> table; 7 char s[1000]; 8 9 int bitcount(int i){ return i == 0? 0: bitcount(i>>1) + (i&1);} 10 11 int main() 12 { 13 int n, x; 14 while(~scanf("%d", &n)){ 15 memset(A, 0, sizeof(A)); 16 table.clear(); 17 for(int i = 0; i < n; i++){ 18 scanf("%s", s); 19 for(int j = 0; s[j]; j++) A[i] ^= (1<<(s[j]-'A')); 20 } 21 int n1 = n>>1; 22 int n2 = n - n1, N1 = (1<<n1); 23 for(int i = 0; i < N1; i++){ 24 x = 0; 25 for(int j = 0; j < n1; j++) if(i & (1<<j)) x ^= A[j]; 26 if(!table[x] || (bitcount(table[x]) < bitcount(i))) table[x] = i; 27 } 28 int ans = 0, N2 = (1<<n2); 29 for(int i = 0; i < N2; i++){ 30 x = 0; 31 for(int j = 0; j < n2; j++) if(i & (1<<j)) x ^= A[j+n1]; 32 if(table[x] && bitcount(ans) < bitcount(i) + bitcount(table[x])) ans = (i<<n1)|table[x];///both | and ^ are right and fast~ 33 } 34 printf("%d\n", bitcount(ans)); 35 for(int i = 0; i < n; i++) if(ans & (1<<i)) printf("%d ", i+1); 36 puts(""); 37 } 38 return 0; 39 } (???(???(???*)?
emm...莫名喜歡狀壓(x).
?
轉(zhuǎn)載于:https://www.cnblogs.com/curieorz/p/9447454.html
總結(jié)
以上是生活随笔為你收集整理的2018/8/9 MultiU 6 并查集+dfs,反向建边提高查询效率 !!! / 最大字段和n维(降维)/ 状压+中途相遇法...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 专访杨开振:程序员除了敲代码还能做什么?
- 下一篇: 容易忽略的URL