【題目連接】html
A. ZOJ 4004 - Easy Number Gamenode
首先確定是選擇值最小的 $2*m$ 進行操做,這些數在操做的時候每次取一個最大的和最小的相乘是最優的。ios
#include <bits/stdc++.h> using namespace std; const int maxn = 100010; int T, n, m; long long a[maxn]; int main() { scanf("%d", &T); while(T--) { scanf("%d%d", &n, &m); long long sum = 0; for(int i = 0; i < n; i ++) { scanf("%lld", &a[i]); } sort(a, a+n); int first = 0, last = m * 2 - 1; while(first < m) { sum = sum + a[first] * a[last]; first++; last--; } printf("%lld\n", sum); } return 0; }
B. ZOJ 4005 - Lucky Manc++
找規律會發現就是求 $\left\lfloor \sqrt { n } \right\rfloor $ 的奇偶性。從這裏學大數開根號。app
#include<cstdio> #include<cstring> #include<iostream> using namespace std; int l;char ans[1000];int num=0; int work(int o,char *O,int I) { char c,*D=O; if(o>0) { for(l=0;D[l];D[l++]-=10) { D[l++]-=120; D[l]-=110; while(!work(0,O,l)) D[l]+=20; //putchar((D[l]+1032)/20); ans[num++]=(D[l]+1032)/20; } //putchar(10); //ans[num++]='1';ans[num++]='0'; } else { c=o+(D[I]+82)%10-(I>l/2)*(D[I-l+I]+72)/10-9; D[I]+=I<0 ? 0 : !(o=work(c/10,O,I-1))*((c+999)%10-(D[I]+92)%10); } return o; } int main() { char s[1200]; int t;scanf("%d",&t); while(t--){ num=0; s[0]='0'; scanf("%s",s+1); if(strlen(s)%2 == 1) work(2,s+1,0); else work(2,s,0); ans[num]='\0'; int tmp=ans[num-1]-'0'; if(tmp&1) printf("1\n"); else printf("0\n"); } return 0; }
C. ZOJ 4006 - Travel along the Lineide
假設往左走了 $x$ 步,停留了 $y$ 步,往右走了 $z$ 步,這種狀況下的機率爲 ${ C }_{ n }^{ x }{ C }_{ n-x }^{ y }{ \left( \frac { 1 }{ 4 } \right) }^{ x+z }{ \left( \frac { 1 }{ 2 } \right) }^{ y }$。枚舉 $x$ 以後,$y$ 和 $z$ 都能肯定。ui
#include <bits/stdc++.h> using namespace std; const int maxn = 6e5 + 10; const int INF = 0x7FFFFFFF; const long long mod = 1e9 + 7; int T, n, m; long long f[maxn], po[maxn]; long long extend_gcd(long long a,long long b,long long &x,long long &y) { if(a==0&&b==0) return -1;//無最大公約數 if(b==0){x=1;y=0;return a;} long long d=extend_gcd(b,a%b,y,x); y-=a/b*x; return d; } long long mod_reverse(long long a,long long n) { long long x,y; long long d=extend_gcd(a,n,x,y); if(d==1) return (x%n+n)%n; else return -1; } long long C(int a, int b) { long long fz = f[a]; long long fm = f[a - b] * f[b] % mod; return fz * mod_reverse(fm, mod) % mod; } void init() { po[0] = 1LL; f[0] = 1LL; for(int i = 1; i < maxn; i ++) { po[i] = (po[i - 1] * 2LL) % mod; f[i] = (f[i - 1] * i) % mod; } } int main() { #ifdef ZHOUZHENTAO freopen("test.in", "r", stdin); #endif init(); scanf("%d", &T); while(T --) { scanf("%d%d", &n, &m); long long ans = 0; for(int x = 0; x <= n; x ++) { int y = n - x - m - x; int z = m + x; if(y < 0 || y > n) continue; if(z < 0 || z > n) continue; long long fz = C(n, x) * C(n - x, y) % mod; long long fm = po[2 * (x + z) + y]; long long tmp = fz * mod_reverse(fm ,mod) % mod; ans = (ans + tmp) % mod; } printf("%lld\n", ans); } return 0; }
D. ZOJ 4007 - Machine Learning on a Treespa
因爲每個節點的 $x_i$ 都相同,所以 $w_{ij}$ 均爲 1。總的來講就是讓你安排每一個節點的權值,若是某條邊兩端點權值不一樣,$value$ 就加 1,問 $value$ 的最小值是多少。.net
將樹有根化後進行 dp,$f[i][j]$ 表示 $i$ 節點的權值設置爲 $j$,以 $i$ 爲根的子樹的最小 $value$ 值。htm
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5 + 10; const int INF = 0x7FFFFFFF; int h[maxn], to[maxn], nx[maxn], sz; int y[maxn]; int T, n; int dp[maxn][2], f[maxn]; void add(int x, int y) { to[sz] = y; nx[sz] = h[x]; h[x] = sz ++; } void dfs(int x) { f[x] = 1; if(y[x] == -1) dp[x][0] = dp[x][1] = 0; else dp[x][y[x]] = 0; for(int i = h[x]; i != -1; i = nx[i]) { if(f[to[i]]) continue; dfs(to[i]); if(y[x] != -1) { dp[x][y[x]] += min(dp[to[i]][y[x]], dp[to[i]][y[x] ^ 1] + 1); } else { dp[x][0] += min(dp[to[i]][0], dp[to[i]][1] + 1); dp[x][1] += min(dp[to[i]][0] + 1, dp[to[i]][1]); } } } int main() { #ifdef ZHOUZHENTAO freopen("test.in", "r", stdin); #endif int T; scanf("%d", &T); while(T --) { scanf("%d", &n); for(int i = 1; i <= n; i ++) { scanf("%d", &y[i]); h[i] = -1; dp[i][0] = dp[i][1] = 5000000; f[i] = 0; } sz = 0; for(int i = 1; i < n; i ++) { int x, y; scanf("%d%d", &x, &y); add(x, y); add(y, x); } dfs(1); for(int i = 1; i <= n; i ++) { // cout << i << " " << dp[i][0] << " " << dp[i][1]<< endl; } printf("%d\n", min(dp[1][0], dp[1][1])); } return 0; }
E. ZOJ 4008 - Yet Another Tree Query Problem
樹上詢問編號在 $[L,R]$ 範圍的節點和邊構成了幾個連通塊。首先 $[L, R]$ 有 $R-L+1$ 個節點,若是咱們可以計算出來 $[L, R]$ 有 $x$ 條邊,那麼答案就是 $R-L+1-x$。因爲是樹,多一條邊就會少一個連通塊。
計算 $[L, R]$ 中的邊的條數就成了一個區間問題。題目中給出的邊至關於 $n-1$ 個區間,問這些區間中有多少區間徹底在 $[L,R]$ 內。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; const int INF = 0x7FFFFFFF; int T, n, q; struct X { int L, R, tp, id; }s[maxn * 4]; int ans[maxn * 4]; int c[maxn * 4]; int lowbit(int x) { return x & (-x); } int sum(int p) { int res = 0; while(p > 0) { res += c[p]; p -= lowbit(p); } return res; } void update(int p) { while(p < 4 * maxn) { c[p] += 1; p += lowbit(p); } } bool cmp(const X& a, const X& b) { if(a.R != b.R) { return a.R < b.R; } else { return a.tp < b.tp; } } int main() { #ifdef ZHOUZHENTAO freopen("test.in", "r", stdin); #endif scanf("%d", &T); while(T--) { scanf("%d%d", &n, &q); for(int i = 1; i < n + q; i ++) { scanf("%d%d", &s[i].L, &s[i].R); if(s[i].L > s[i].R) { swap(s[i].L, s[i].R); } if(i < n) s[i].tp = 0; else s[i].tp = 1; s[i].id = i; } int u = 0; sort(s + 1, s + n + q, cmp); for(int i = 1; i < n + q; i ++) { // cout << s[i].L << " " << s[i].R << " " << s[i].tp << " " << s[i].id << endl;; } memset(c, 0, sizeof c); for(int i = 1; i < n + q; i ++) { if(s[i].tp == 0) { u ++; update(s[i].L); } else { ans[s[i].id] = (s[i].R - s[i].L + 1) - (u - sum(s[i].L - 1)); } } for(int i = n; i < n + q; i ++) { printf("%d\n", ans[i]); } } return 0; }
F. ZOJ 4009 - And Another Data Structure Problem
全部數據在變 48 次以後,就會變回一開始的數,所以循環節爲 48,線段樹每一個節點存 48 種狀態便可。
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int mod = 99971; const int maxn = 1e5 + 10; int T, n, q; int s[maxn * 4][50]; int f[maxn * 4], p[maxn * 4]; int nx[maxn]; void init() { for(int i = 0; i < 99971; i ++) { nx[i] = 1; for(int j = 0; j < 3; j ++) { long long tmp = (1LL * nx[i] * 1LL * i) % (long long)mod; nx[i] = (int)tmp; } } } void pushUp(int rt) { for(int i = 0; i < 48; i ++) { s[rt][i] = (s[2 * rt][(p[2 * rt] + i) % 48] + s[2 * rt + 1][(p[2 * rt + 1] + i) % 48]) % mod; } p[rt] = 0; } void pushDown(int rt) { if(f[rt] == 0) return; f[2 * rt] += f[rt]; f[2 * rt + 1] += f[rt]; p[2 * rt] = (p[2 * rt] + f[rt]) % 48; p[2 * rt + 1] = (p[2 * rt + 1] + f[rt]) % 48; f[rt] = 0; } void build(int l, int r, int rt) { p[rt] = f[rt] = 0; if(l == r) { scanf("%d", &s[rt][0]); s[rt][0] = s[rt][0] % mod; for(int i = 1; i < 48; i ++) { s[rt][i] = nx[s[rt][i - 1]]; } return; } int mid = (l + r) / 2; build(l, mid, 2 * rt); build(mid + 1, r, 2 * rt + 1); pushUp(rt); } void update(int L, int R, int l, int r, int rt) { if(L <= l && r <= R) { p[rt] = (p[rt] + 1) % 48; f[rt] = f[rt] + 1; return; } pushDown(rt); int mid = (l + r) / 2; if(L <= mid) update(L, R, l, mid, 2 * rt); if(R > mid) update(L, R, mid + 1, r, 2 * rt + 1); pushUp(rt); } int sum(int L, int R, int l, int r, int rt) { if(L <= l && r <= R) { return s[rt][p[rt]]; } int left = 0; int right = 0; pushDown(rt); int mid = (l + r) / 2; if(L <= mid) left = sum(L, R, l, mid, 2 * rt); if(R > mid) right = sum(L, R, mid + 1, r, 2 * rt + 1); pushUp(rt); return (left + right) % mod; } int main() { init(); scanf("%d", &T); while(T --) { scanf("%d%d", &n, &q); build(1, n, 1); while(q --) { int op, L, R; scanf("%d%d%d", &op, &L, &R); if(op == 1) { update(L, R, 1, n, 1); } else { printf("%d\n", sum(L, R, 1, n, 1)); } } } return 0; }
G. ZOJ 4010 - Neighboring Characters
看了 cxhscst2 的題解恍然大悟。
刪除連續 $k$ 個字符,至關於留下連續的 $p=len-k$ 個字符,咱們能夠從留下來的角度來考慮問題。
先把環拆了,也就是字符串擴大一倍,這樣環上一段連續的區間就對應於字符串上一段連續的區間;一樣的地,字符串上一段連續的區間也對應於環上一段連續的區間。
而後要去看是否存在長度爲 $p(1\le p\le len)$ 的子串,相鄰和首尾的字符均不一樣。
接下來感受是本題精髓:
假設位置 1 最右能擴展到位置 $a$,保證 $[1,a]$ 相鄰字符都不相同(首尾先無論)
假設位置 $a+1$ 最右能擴展到位置 $b$,保證 $[a+1,b]$ 相鄰字符都不相同(首尾先無論)
假設位置 $b+1$ 最右能擴展到位置 $c$,保證 $[b+1,c]$ 相鄰字符都不相同(首尾先無論)
....
假設位置 $x+1$ 最右能擴展到位置 $len$,保證 $[x+1,len]$ 相鄰字符都不相同(首尾先無論)
咱們把 "長度爲 $p(1\le p\le len)$ 的子串,相鄰和首尾的字符均不一樣" 的子串稱爲咱們須要尋找的串。
那麼,咱們須要尋找的串要麼沒有,要麼必然出如今 $[1,a]$,$[a+1,b]$,....,$[x+1,len]$ 其中的一個或多箇中。
爲何不用考慮 $[2, ?]$,由於 ?最大也只有 $a$,因此考慮 $[1,a]$ 足夠了。
下面以 $[1,a]$ 爲例,以後的都是同樣的操做。
要在 $[1,a]$ 中尋找是否存在長度爲 $p$ 的咱們須要尋找的串。首先,$[1,a]$ 中任意一個長度爲 $p$ 的子串,相鄰都是不一樣的,咱們只需判斷首尾。
也就是若是知足如下其中的一項,咱們就找到了長度爲 $p$ 的咱們須要找的串。
$s[1]\neq s[p]\\ s[2]\neq s[p+1]\\ s[3]\neq s[p+2]\\ ...\\ s[a-p+1]\neq s[a]$
觀察一下能夠發現,不等式左邊造成了一個前綴,右邊是一個後綴,也就是說若是前綴不等於後綴,就有知足條件的,所以字符串 hash 判斷一下先後綴是否相同便可。
複雜度的話是 $O(n)$;由於 $[1,a]$,$[a+1,b]$,....,$[x+1,len]$ 這些區間無相交部分,每一個區間內枚舉一遍,因此複雜度是線性的。
#include <bits/stdc++.h> using namespace std; const int maxn = 2e6 + 10; const int INF = 0x7FFFFFFF; char s[maxn]; int ans[maxn]; long long base = 131; long long mod = 1e9 + 7; long long h[maxn], po[maxn]; long long H(int L, int R) { L ++; R ++; long long res = 0; long long tmp = h[L - 1] * po[R - L + 1] % mod; res = (h[R] - tmp + mod) % mod; return res; } int main() { #ifdef ZHOUZHENTAO freopen("test.in", "r", stdin); #endif po[0] = 1LL; for(int i = 1; i < maxn; i ++) { po[i] = (po[i - 1] * base) % mod; } int T; scanf("%d", &T); while(T --) { scanf("%s", s); int len = strlen(s); for(int i = len; i < 2 * len; i ++) { s[i] = s[i - len]; s[i + 1] = 0; } len = 2 * len; for(int i = 0; i < len; i ++) { h[i + 1] = h[i] * base % mod; h[i + 1] = (h[i + 1] + s[i]) % mod; } //printf("%s\n", s); for(int i = 0; i <= len; i ++) { ans[i] = 0; } for(int i = 0, j = 0; i < len; i = j + 1) { for(j = i; j < len && j + 1 < len && s[j] != s[j + 1]; j ++) {} //cout << i << " " << j << endl; int t = j - i + 1; t = min(t, len / 2); for(int x = 1; x <= t; x ++) { if(x == 1) { ans[x] = 1; } else if(H(i, j - x + 1) != H(i + x - 1, j)) { ans[x] = 1; } } } for(int i = len / 2; i >= 1; i --) { printf("%d", ans[i]); } printf("\n"); } return 0; }
H. ZOJ 4011 - Happy Sequence
$f[i][j]$ 表示放了 $i$ 個數,最後一個數是 $j$ 的方案數。轉移的時候枚舉一下約數就能夠了。
#include <bits/stdc++.h> using namespace std; const long long mod = 1e9 + 7; int T, n, m; long long dp[2010][2010]; vector<int> vec[2010]; int main() { for(int i = 1; i <= 2000; i++) { for(int j = 1; j <= i; j ++) { if(i % j == 0) { vec[i].push_back(j); } } } scanf("%d", &T); while(T --) { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i ++) { dp[1][i] = 1; } for(int i = 2; i <= m; i ++) { for(int j = 1; j <= n; j ++) { dp[i][j] = 0; for(int k = 0; k < vec[j].size(); k ++) { dp[i][j] = (dp[i][j] + dp[i - 1][vec[j][k]]) % mod; } } } long long ans = 0; for(int j = 1; j <= n; j ++) { ans = (ans + dp[m][j]) % mod; } printf("%lld\n", ans); } return 0; }
I. ZOJ 4012 - Your Bridge is under Attack
用 KD-Tree 切割一下平面,每一個節點算一下包圍住這個平面的最小矩形座標,每次詢問的時候若是發現這條線和這個矩形無相交部分,直接 return。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; const int INF = 0x7FFFFFFF; const int demension = 2;//二維 struct node { int pos[demension]; int l, r; int mi[2], ma[2]; }a[maxn]; int cmpDem; int root; int n, q, ans, A, B; bool cmp(const node &a, const node&b) { return a.pos[cmpDem] < b.pos[cmpDem]; } void Merge(int k) { for (int i = 0; i < demension; i++) { a[k].ma[i] = a[k].pos[i]; a[k].mi[i] = a[k].pos[i]; if (a[k].l) { a[k].ma[i] = max(a[k].ma[i], a[a[k].l].ma[i]); a[k].mi[i] = min(a[k].mi[i], a[a[k].l].mi[i]); } if (a[k].r) { a[k].ma[i] = max(a[k].ma[i], a[a[k].r].ma[i]); a[k].mi[i] = min(a[k].mi[i], a[a[k].r].mi[i]); } } } int build(int l, int r, int k) { if (l > r) return 0; int mid = (l + r) / 2; //以第mid個元素爲中心排序 cmpDem = k; nth_element(a + l, a + mid, a + r + 1, cmp); //左右子樹 a[mid].l = build(l, mid - 1, (k + 1) % demension); a[mid].r = build(mid + 1, r, (k + 1) % demension); Merge(mid); return mid; } int G(int x1, int y1, int x2, int y2, int x3, int y3) { if(1LL * (x2 - x1) * (y3 - y2) == 1LL * (x3 - x2) * (y2 - y1)) return 1; return 0; } int ok(int id) { int x1 = A, x2 = 0, x3 = a[id].pos[0]; int y1 = 0, y2 = B, y3 = a[id].pos[1]; return G(x1, y1, x2, y2, x3, y3); } struct Point { long long x; long long y; Point(int xx, int yy) { x = 1LL * xx; y = 1LL * yy; } }; bool lineIntersectSide(Point A, Point B, Point C, Point D) { // A(x1, y1), B(x2, y2)的直線方程爲: // f(x, y) = (y - y1) * (x1 - x2) - (x - x1) * (y1 - y2) = 0 long long fC = (C.y - A.y) * (A.x - B.x) - (C.x - A.x) * (A.y - B.y); long long fD = (D.y - A.y) * (A.x - B.x) - (D.x - A.x) * (A.y - B.y); if(fC > 0 && fD > 0) return false; if(fC < 0 && fD < 0) return false; return true; } bool sideIntersectSide(Point A, Point B, Point C, Point D) { if(!lineIntersectSide(A, B, C, D)) return false; if(!lineIntersectSide(C, D, A, B)) return false; return true; } int check(int id) { // [mi[0], mi[1]], [ma[0], ma[1]] // [A, 0], [0, B] if(G(A, 0, 0, B, a[id].mi[0], a[id].mi[1])) return 1; if(G(A, 0, 0, B, a[id].ma[0], a[id].ma[1])) return 1; if(sideIntersectSide(Point(A, 0), Point(0, B), Point(a[id].mi[0], a[id].mi[1]), Point(a[id].ma[0], a[id].ma[1]))) { return 1; } return 0; } void ask(int l, int r, int id) { if(id == 0) return; if(check(id) == 0) return; ans = ans + ok(id); int mid = (l + r) / 2; ask(l, mid - 1, a[id].l); ask(mid + 1, r, a[id].r); } int main() { #ifdef ZHOUZHENTAO freopen("test.in", "r", stdin); #endif int T; scanf("%d", &T); while(T --) { scanf("%d%d", &n, &q); for(int i = 1; i <= n; i ++) { scanf("%d%d", &a[i].pos[0], &a[i].pos[1]); } root = build(1, n, 0); /* for(int i = 1; i <= n; i ++) { printf("%d : %d %d mi[0]:%d mi[1]:%d ma[0]:%d ma[1]:%d\n", i, a[i].pos[0], a[i].pos[1], a[i].mi[0], a[i].mi[1], a[i].ma[0], a[i].ma[1]); } */ while(q --) { scanf("%d%d", &A, &B); ans = 0; ask(1, n, root); printf("%d\n", ans); } } return 0; }
J. ZOJ 4013 - Super Brain
這個題比較簡單,有不少方法判吧。
#include <bits/stdc++.h> using namespace std; int T, n; int main() { scanf("%d", &T); while(T--) { scanf("%d", &n); set<int> st; for(int i = 1; i <= n; i ++) { int x; scanf("%d", &x); st.insert(x); } int ans; for(int i = 1; i <= n; i ++) { int x; scanf("%d", &x); if(st.count(x) > 0) ans = x; } printf("%d\n", ans); } return 0; }