hdu6370 Werewolf
http://acm.hdu.edu.cn/showproblem.php?pid=6370
题意:村民只能说真话,狼人“可以”撒谎,每个人说一句话指向出自己之外任意一人身份,问与多少铁民和铁狼。
思路:1.因为可以有全狼的情况,所以不存在铁民。
2.通过基环树找狼。(具体参见下图,搬运于别人的博客QAQ。。。)
当我们发现有连续个点是有村民的边,如点1,点2,点3,点4,点5,点6;而这些个连续的其中一个点(如图中的点6)有一条狼人边连到了这些个连续的其他的点(如上图连到了点1)。
此时,我们用反证法可以证明,倘若1号点是村民,则根据村民不会说谎的性质可以判断出1到6号点全是村民,而根据村民不会说谎的性质,只能证明出1号点必为狼人。此时我们同时也可以发现,倘若1号点是狼人,则根据狼人会说谎的性质可知,指向1号点为村民的也必定是狼人。
因此我们的算法雏形就初步显现出来了。
我们要维护一些连续的村民点,可以用一个并查集进行维护。我们可以将村民边上的两个点不断的用并查集去合并,而当我们遍历狼边的时候,倘若我们发现狼边上的两个点都在一个集合中,则说明必定满足上述的情况,则我们不断遍历这条狼人边所指向的那个结点(如上图的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 }
打多校的几场下来,就是a完水题就挂机,然后赛后看题解,有的时候看完题解还是不知道该怎么写,于是看别人的代码,于是于是。。。依然啥也不会。。。(怨念。。o(一︿一+)o)
颓废度日,看底特律到睡着后决定补大白上的题然后。。。QAQ。。。lrj太强了!!!伏地膜!!!啊啊啊啊啊人家写出来的程序怎么这么xxxxxxx!!!x~
uva10755 G - Garbage Heap
题意:求解最大子“立方体”和。
思路:三位最大子段和,降维->二维,暴力枚举子矩阵O(n^4),第三维度采取最大子段和O(n)的dp解决,总复杂度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 }
学习了lrj 的程序,思路清晰,解法实用有效,很容易将问题推广到四维或更高维度的情形。
H - Jurassic Remains(UVALive - 2965)
题意:n(<=24 24/2=12可状压)个字符串,选尽量多个串使得每个大写字母(26个字母, 可状压)都出现偶数次。
思路:1.将每个串转化为二进制,出现偶数次即为该位xor后为0。
2.将字符串集合划分为两个集合,也转化为二进制,则有AxorB=0 -> A = B。(A、B为两集合中所有二进制异或和)。
3.对于每个A=B,找到包含字符串数目最多(二进制中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).