HZNU Training 26 for Zhejiang Provincial Competition 2020
POJ 3076 Sudoku
思路:
dfs + 剪枝
首先,如果这个位置只能填一种字母,那就直接填
其次,如果对于每一种字母,如果某一列或者某一行或者某一块只能填它,那就填它
然后,对于某个位置如果不能填字母了,或者某种字母在一行一列或一块中出向了两次以上,说明当前方案不成立
最后贪心地从可选情况少的往下搜
#include<iostream> #include<cstring> #include<cmath> #include<queue> #include<stack> #include<list> #include<map> #include<set> #include<sstream> #include<string> #include<vector> #include<cstdio> #include<ctime> #include<bitset> #include<algorithm> #include<string.h> #define L(x) (1 << (x)) using namespace std; typedef long long ll; #define lson l , mid , rt << 1 #define rson mid + 1 , read , rt << 1 | 1 ll read() { ll x = 0, f = 1; char ch = getchar(); while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); } while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } const int maxn = 20; int mp[maxn][maxn]; int st[maxn][maxn]; int sum = 0; char s[maxn][maxn + 10]; void add(int x, int y, int t) { mp[x][y] = t; sum++; for (int i = 1; i <= 16; i++) { st[i][y] |= 1 << t - 1; st[x][i] |= 1 << t - 1; } int xx = (x + 3) / 4, yy = (y + 3) / 4; for (int i = (xx - 1) * 4 + 1; i <= xx * 4; i++) { for (int j = (yy - 1) * 4 + 1; j <= yy * 4; j++) { st[i][j] |= 1 << t - 1; } } } void print() { for (int i = 1; i <= 16; i++) { for (int j = 1; j <= 16; j++) { putchar(mp[i][j] - 1 + 'A'); } puts(""); } puts(""); } bool dfs() { if (sum == 256) { print(); return true; } for (int i = 1; i <= 16; i++) { for (int j = 1; j <= 16; j++) { if (!mp[i][j]) { int cnt = 0, t = 0; for (int k = 1; k <= 16; k++) { if ((st[i][j] & (1 << k - 1)) == 0) { cnt++; t = k; if (cnt == 2) break; } } if (!cnt) return false; if (cnt == 1) add(i, j, t); } } } for (int i = 1; i <= 16; i++) { for (int k = 1; k <= 16; k++) { int cnt1 = 0, cnt2 = 0, y; for (int j = 1; j <= 16; j++) { if (mp[i][j] == k) cnt1++; if (cnt1 == 2) return false; if (!mp[i][j] && (st[i][j] & (1 << k - 1)) == 0) cnt2++, y = j; } if (!cnt1 && !cnt2) return false; if (!cnt1 && cnt2 == 1) add(i, y, k); } } for (int j = 1; j <= 16; j++) { for (int k = 1; k <= 16; k++) { int cnt1 = 0, cnt2 = 0, x; for (int i = 1; i <= 16; i++) { if (mp[i][j] == k) cnt1++; if (cnt1 == 2) return false; if (!mp[i][j] && (st[i][j] & (1 << k - 1)) == 0) cnt2++, x = i; } if (!cnt1 && !cnt2) return false; if (!cnt1 && cnt2 == 1) add(x, j, k); } } for (int i = 1; i <= 16; i++) { int x = (i + 3) / 4, y = i - (x - 1) * 4; for (int k = 1; k <= 16; k++) { int cnt1 = 0, cnt2 = 0, xx, yy; for (int ii = (x - 1) * 4 + 1; ii <= x * 4; ii++) { for (int jj = (y - 1) * 4 + 1; jj <= y * 4; jj++) { if (mp[ii][jj] == k) cnt1++; if (cnt1 == 2) return false; if (!mp[ii][jj] && (st[ii][jj] & (1 << k - 1)) == 0) cnt2++, xx = ii, yy = jj; } } if (!cnt1 && !cnt2) return false; if (!cnt1 && cnt2 == 1) add(xx, yy, k); } } if (sum == 256) { print(); return true; } int mn = maxn, x, y; for (int i = 1; i <= 16; i++) { for (int j = 1; j <= 16; j++) { if (!mp[i][j]) { int cnt = 0; for (int k = 1; k <= 16; k++) { if ((st[i][j] & (1 << k - 1)) == 0) { cnt++; if (cnt >= mn) break; } } if (cnt < mn) { mn = cnt; x = i; y = j; } } } } int tst[maxn][maxn], tmp[maxn][maxn]; memcpy(tst, st, sizeof(st)); memcpy(tmp, mp, sizeof(mp)); int tsum = sum; for (int k = 1; k <= 16; k++) { if ((st[x][y] & (1 << k - 1)) == 0) { add(x, y, k); bool f = dfs(); if (!f) { memcpy(st, tst, sizeof(tst)); memcpy(mp, tmp, sizeof(tmp)); sum = tsum; } else return true; } } return false; } int main() { while (scanf("%s", s[1] + 1) != EOF) { for (int i = 2; i <= 16; i++) { scanf("%s", s[i] + 1); } sum = 0; memset(mp, 0, sizeof mp); memset(st, 0, sizeof st); for (int i = 1; i <= 16; i++) { for (int j = 1; j <= 16; j++) { if (isalpha(s[i][j])) add(i, j, s[i][j] - 'A' + 1); } } dfs(); } return 0; }
POJ 1741 Tree
题目大意
多组测试数据,每次输入n、m,和一棵n个点的有边权的树,问你满足x到y距离小于等于m的无序点对(x,y)的个数是多少。
题解
树的点分治模板题
考虑到路径只有两种情况,一是经过根节点,二是不经过根节点。
如果不经过根节点,那么一定经过最小公共子树的根节点,可以转化为问题一的子问题。
于是考虑怎么递归解决问题一。
对于根节点进行一次dfs,求出deep,并将其从小到大排序。
避免重复,只需要求出其中deep[x]≤deep[y]且deep[x]+deep[y]≤m的个数。
用i表示左指针,j表示右指针,i从左向右遍历。
如果deep[i]+deep[j]≤m,则点对(i,t)(i<t≤j)都符合题意,将j-i加入答案中,并且i++;否则j--。
然而这样还会重复计算在同一棵子树中的点对,所以再进行下一步dfs之前需要减去重复部分。
因为树可能会退化,导致选择链头时时间复杂度极大而TLE。
于是每次不能固定选择root,而是以重心作为root去处理,这样能保证时间复杂度再O(nlog2n)以下。
#include<iostream> #include<cstring> #include<cmath> #include<queue> #include<stack> #include<list> #include<map> #include<set> #include<sstream> #include<string> #include<vector> #include<cstdio> #include<ctime> #include<bitset> #include<algorithm> #include<string.h> #define L(x) (1 << (x)) using namespace std; typedef long long ll; #define lson l , mid , rt << 1 #define rson mid + 1 , read , rt << 1 | 1 ll read() { ll x = 0, f = 1; char ch = getchar(); while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); } while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } const int maxn = 10010; int m; int head[maxn], to[maxn << 1], len[maxn << 1], net[maxn << 1]; int cnt, si[maxn], deep[maxn], root, vis[maxn], f[maxn], sn, d[maxn]; int tot, ans; void add(int x, int y, int z) { to[++cnt] = y, len[cnt] = z, net[cnt] = head[x], head[x] = cnt; } void getroot(int x, int fa) { f[x] = 0, si[x] = 1; int i; for (i = head[x]; i; i = net[i]) if (to[i] != fa && !vis[to[i]]) getroot(to[i], x), si[x] += si[to[i]], f[x] = max(f[x], si[to[i]]); f[x] = max(f[x], sn - si[x]); if (f[root] > f[x]) root = x; } void getdeep(int x, int fa) { d[++tot] = deep[x]; int i; for (i = head[x]; i; i = net[i]) if (to[i] != fa && !vis[to[i]]) deep[to[i]] = deep[x] + len[i], getdeep(to[i], x); } int calc(int x) { tot = 0, getdeep(x, 0), sort(d + 1, d + tot + 1); int i = 1, j = tot, sum = 0; while (i < j) { if (d[i] + d[j] <= m) sum += j - i, i++; else j--; } return sum; } void dfs(int x) { deep[x] = 0, vis[x] = 1, ans += calc(x); int i; for (i = head[x]; i; i = net[i]) if (!vis[to[i]]) deep[to[i]] = len[i], ans -= calc(to[i]), sn = si[to[i]], root = 0, getroot(to[i], 0), dfs(root); } int main() { int n, i, x, y, z; while (scanf("%d%d", &n, &m) && (n || m)) { memset(head, 0, sizeof(head)); memset(vis, 0, sizeof(vis)); cnt = 0, ans = 0; for (i = 1; i < n; i++) scanf("%d%d%d", &x, &y, &z), add(x, y, z), add(y, x, z); f[0] = 0x7fffffff, sn = n; root = 0, getroot(1, 0), dfs(root); printf("%d\n", ans); } return 0; }
POJ 1852 Ants
题目大意
在一根长为L的水平木棍上有一群数量为n的蚂蚁,它们以每秒1cm/s的速度走到木棍一端就会掉下去。
现在知道它们的起始位置是距离木根左端点的x处。但是不知道它们爬行的方向。
在相向而行的两只蚂蚁相遇后,它们会掉头往反方向走。问所有蚂蚁都落下木棍的最快时间和最慢时间。
题解
因为是同时出发的,相遇时的两只蚂蚁用的时间是相同的,我们可以无视蚂蚁的区别,当两只蚂蚁相遇时它们保持原样交错而行。
这样每只蚂蚁都是独立运动的,那么只要找每只蚂蚁掉下去的时间就行了。
#include<iostream> #include<cstring> #include<cmath> #include<queue> #include<stack> #include<list> #include<map> #include<set> #include<sstream> #include<string> #include<vector> #include<cstdio> #include<ctime> #include<bitset> #include<algorithm> #include<string.h> #define L(x) (1 << (x)) using namespace std; typedef long long ll; #define lson l , mid , rt << 1 #define rson mid + 1 , read , rt << 1 | 1 ll read() { ll x = 0, f = 1; char ch = getchar(); while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); } while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } const int maxn = 1000010; int a[maxn]; int ansmin, ansmax, L, n; int main() { int t; scanf("%d", &t); while (t--) { scanf("%d%d", &L, &n); for (int i = 0; i < n; ++i) { scanf("%d", &a[i]); } int minn; ansmin = -1; for (int i = 0; i < n; ++i) { minn = min(a[i], L - a[i]); if (minn > ansmin) ansmin = minn; } printf("%d ", ansmin); int maxx; ansmax = -1; for (int i = 0; i < n; ++i) { maxx = max(a[i], L - a[i]); if (maxx > ansmax) ansmax = maxx; } printf("%d\n", ansmax); } return 0; }
POJ 2407 Relatives
裸的欧拉函数,直接用公式
#include<iostream> #include<cstring> #include<cmath> #include<queue> #include<stack> #include<list> #include<map> #include<set> #include<sstream> #include<string> #include<vector> #include<cstdio> #include<ctime> #include<bitset> #include<algorithm> #include<string.h> #define L(x) (1 << (x)) using namespace std; typedef long long ll; #define lson l , mid , rt << 1 #define rson mid + 1 , read , rt << 1 | 1 ll read() { ll x = 0, f = 1; char ch = getchar(); while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); } while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } int Q(int n) { int ans = n; for (int i = 2; i <= n; i++){ if (n%i == 0){ ans -= ans / i; while (n%i == 0) { n /= i; } } if (n == 1) break; } return ans; } int main() { int n; while (cin >> n && n){ cout << Q(n) << endl; } return 0; }