..拿金了 沒給學校丟臉
A
....SB題啊 比賽的時候都沒看 裸的一個bitset前綴和
先開一個1e4*1e4的二維bitset數組 初始第i個數組的值為1 << i (即B[i]=1 B[i]<<=i)
很容易我們可以知道要單獨翻轉某一位而不去影響其他位的話 方法是唯一的
然后我們從可以后往前DP 就可以知道要單獨翻轉某一位的話需要翻轉那些位
最后的每次翻轉過后的答案就是 上一個答案^PreL-1 ^PreR
注意用cout的話容易System Error......(再噴一次OJ

/*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}}; const int mod = 1e9 + 7, gakki = 5 + 2 + 1 + 19880611 + 1e9; const int MAXN = 1e5 + 5, MAXM = 1e5 + 5, N = 1e4 + 5; const int MAXQ = 100010; int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], tot = 1; inline void addedge(int u, int v) { to[++tot] = v; nxt[tot] = Head[u]; Head[u] = tot; } inline void read(int &v) { v = 0; char c = 0; int p = 1; while (c < '0' || c > '9') { if (c == '-') { p = -1; } c = getchar(); } while (c >= '0' && c <= '9') { v = (v << 3) + (v << 1) + c - '0'; c = getchar(); } v *= p; } int n, m, l, r; bitset<N> B[10005], pre[10005], ans; int main() { ios_base::sync_with_stdio(0); cin.tie(0); int T; read(T); while (T--) { ans.reset(); read(n), read(m); for (int i = 1; i <= n; i++) { B[i].reset(); } B[0] = 1; for (int i = n; i >= 1; i--) { B[i] = 1; B[i] <<= i; for (int j = 2 * i; j <= n; j += i) { B[i] = B[i] ^ B[j]; } } pre[0] = 0; for (int i = 1; i <= n; i++) { pre[i] = pre[i - 1] ^ B[i]; } for (int i = 1; i <= m; i++) { read(l), read(r); ans = ans ^ pre[l - 1] ^ pre[r]; printf("%d\n", ans.count()); } } return 0; }
B(比賽通過)
水題
C(比賽通過)
把不同的字符串看作是點
用AC自動機建邊 跑一遍即可
D
最后沒有時間了 沒做出來 其實蠻簡單的
在沒有確定根之前 滿足一對pilot要求的點是這兩個點的子樹
在確定了一個節點為根之后 就可以分為兩種情況
1.兩個pilot不是一條鏈上的 則兩個點的子樹+1
2.兩個pilot是一條鏈上的 則先全部節點+1 再把父親到這個兒子的子樹(不包括父親節點)-1 再把兒子的子樹+1
最后看值為m的點有多少個 即為答案+1 -1的這些操作用一個數組維護 dfs從父親到兒子作前綴和即可
可以倍增(NlogN)做 也可以tarjan+dfs序(N)
倍增:
加讀入掛的話 可以減到5500ms

/*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}}; const int mod = 1e9 + 7, gakki = 5 + 2 + 1 + 19880611 + 1e9; const int MAXN = 1e5 + 5, MAXM = 1e5 + 5; const int maxl = 25; const int MAXQ = 100010; int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], tot = 1; inline void addedge(int u, int v) { to[++tot] = v; nxt[tot] = Head[u]; Head[u] = tot; } int flag = 0; int n, m, anser = 0; int presum[MAXN]; int grand[MAXN][maxl]; //x向上跳2^i次方的節點,x到他上面祖先2^i次方的距離 int gw[MAXN][maxl]; //維護距離的數組 int depth[MAXN];//深度 int root; int N; //N的意思是最多能跳幾層 void dfs(int x)//dfs建圖 { for (int i = 1; i <= N; i++) //第一個幾點就全部都是0,第二個節點就有變化了,不理解的話建議復制代碼輸出下這些數組 { grand[x][i] = grand[grand[x][i - 1]][i - 1]; //倍增 2^i=2^(i-1)+2^(i-1) } for (int i = Head[x]; i; i = nxt[i]) { int v = to[i]; if (v != grand[x][0]) { depth[v] = depth[x] + 1; grand[v][0] = x; dfs(v); } } } void Init() { tot = 1; anser = 0; for (int i = 1; i <= n; i++) { presum[i] = Head[i] = 0; } N = floor(log(n + 0.0) / log(2.0));//最多能跳的2^i祖先 depth[root] = 0; //根結點的祖先不存在,用-1表示 depth[0] = -1; memset(grand, 0, sizeof(grand)); } int lca(int a, int b) { if (depth[a] > depth[b]) { swap(a, b); //保證a在b上面,便於計算 } int ans = 0; for (int i = N; i >= 0; i--) //類似於二進制拆分,從大到小嘗試 { if (depth[a] < depth[b] && depth[grand[b][i]] >= depth[a]) //a在b下面且b向上跳后不會到a上面 { b = grand[b][i]; //先把深度較大的b往上跳 } } if (a == b) { return a; } for (int j = N; j >= 0; j--) //在同一高度了,他們一起向上跳,跳他們不相同節點,當全都跳完之后grand【a】【0】就是lca,上面有解釋哈。 { if (grand[a][j] != grand[b][j]) { a = grand[a][j]; b = grand[b][j]; } } return grand[a][0]; } int lca2(int a, int b) { if (depth[a] > depth[b]) { swap(a, b); //保證a在b上面,便於計算 } int ans = 0; for (int i = N; i >= 0; i--) //類似於二進制拆分,從大到小嘗試 { if (depth[a] + 1 < depth[b] && depth[grand[b][i]] >= depth[a] + 1) //a在b下面且b向上跳后不會到a上面 { b = grand[b][i]; //先把深度較大的b往上跳 } } return b; } void getadd(int x, int fa) { presum[x] += presum[fa]; for (int i = Head[x]; i; i = nxt[i]) { int v = to[i]; if (v != fa) { getadd(v, x); } } } int main() { ios_base::sync_with_stdio(0); cin.tie(0); root = 1; int T; scanf("%d",&T); while (T--) { int u, v; scanf("%d %d", &n, &m); Init(); for (int i = 1; i <= n - 1; i++) { scanf("%d %d", &u, &v); addedge(u, v), addedge(v, u); } dfs(root); for (int i = 1; i <= m; i++) { scanf("%d %d", &u, &v); int now = lca(u, v); //cout << "lca" << now << endl; if (now == u || now == v) { int cnt = lca2(u, v); //cout << "lca2 " << cnt << endl; if (now == u) { presum[1]++; presum[cnt]--; presum[v]++; } else { presum[1]++; presum[cnt]--; presum[u]++; } } else { presum[u]++, presum[v]++; } } getadd(1, 0); // for (int i = 1; i <= n; i++) // { // cout << "presum" << i << " " << presum[i] << endl; // } for (int i = 1; i <= n; i++) { if (presum[i] == m) { //cout << i << endl; anser++; } } cout<<anser<<endl; } return 0; }
tarjan做法:
沒有用到dfs序 只是dfs的時候記錄了一下當前節點的兒子節點QQQnxt[u]=v
極限可以做到2500ms
更新:莫名其妙他們的OJ變快(正常)了 跑了340ms 上面的倍增則跑了1000ms

/*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}}; const int mod = 1e9 + 7, gakki = 5 + 2 + 1 + 19880611 + 1e9; const int MAXN = 1e5 + 5, MAXM = 1e5 + 5, N = 1e5 + 5; const int MAXQ = 100010; int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], tot = 1; inline void addedge(int u, int v) { to[++tot] = v; nxt[tot] = Head[u]; Head[u] = tot; } inline void read(int &v) { v = 0; char c = 0; int p = 1; while (c < '0' || c > '9') { if (c == '-') { p = -1; } c = getchar(); } while (c >= '0' && c <= '9') { v = (v << 3) + (v << 1) + c - '0'; c = getchar(); } v *= p; } int QQQnxt[MAXN]; int QQQanser[MAXN]; pair<int, int> QQQ[MAXM]; int n, m, anser = 0; int presum[MAXN], ans[MAXN]; bool vis[MAXN];//訪問標記 int ancestor[MAXN];//祖先 struct Query { int q, next; int index;//查詢編號 } query[MAXQ * 2]; int tt, Q, h[MAXQ], answer[MAXQ]; int F[MAXN];//需要初始化為-1 int find(int x) { if (F[x] == -1) { return x; } return F[x] = find(F[x]); } void bing(int u, int v) { int t1 = find(u); int t2 = find(v); if (t1 != t2) { F[t1] = t2; } } inline void add_query(int u, int v, int index) { query[tt].q = v; query[tt].next = h[u]; query[tt].index = index; h[u] = tt++; query[tt].q = u; query[tt].next = h[v]; query[tt].index = index; h[v] = tt++; } void LCA(int u) { ancestor[u] = u; vis[u] = true; for (int i = Head[u]; i; i = nxt[i]) { int v = to[i]; QQQnxt[u] = v; if (vis[v]) { continue; } LCA(v); bing(u, v); ancestor[find(u)] = u; } for (int i = h[u]; i != -1; i = query[i].next) { int v = query[i].q; if (vis[v]) { answer[query[i].index] = ancestor[find(v)]; if (answer[query[i].index] == v) { QQQanser[query[i].index] = QQQnxt[v]; } } } } void init() { tt = tot = 1; anser = 0; for (int i = 1; i <= n; i++) { ancestor[i] = presum[i] = Head[i] = 0; vis[i] = F[i] = h[i] = -1; } } void getadd(int x, int fa) { presum[x] += presum[fa]; for (int i = Head[x]; i; i = nxt[i]) { int v = to[i]; if (v != fa) { getadd(v, x); } } } int main() { ios_base::sync_with_stdio(0); cin.tie(0); int T; read(T); while (T--) { int u, v; read(n), read(m); init(); for (int i = 1; i <= n - 1; i++) { read(u), read(v); addedge(u, v), addedge(v, u); } for (int i = 0; i < m; i++) { read(u), read(v); add_query(u, v, i); QQQ[i] = make_pair(u, v); } LCA(1); for (int i = 0; i < m; i++) { //cout << answer[i] << endl; if (answer[i] == QQQ[i].first) { //cout<<QQQanser[i]<<endl; presum[1]++; presum[QQQanser[i]]--; presum[QQQ[i].second]++; } else if (answer[i] == QQQ[i].second) { //cout<<QQQanser[i]<<endl; presum[1]++; presum[QQQanser[i]]--; presum[QQQ[i].first]++; } else { presum[QQQ[i].first]++; presum[QQQ[i].second]++; } } getadd(1, 0); // for (int i = 1; i <= n; i++) // { // cout << "presum" << i << " " << presum[i] << endl; // cout << "ans" << i << " " << ans[i] << endl; // } for (int i = 1; i <= n; i++) { if (presum[i] == m) { //cout << i << endl; anser++; } } printf("%d\n", anser); } return 0; }
E(比賽通過)
注意年=月=日的特殊合法情況即可
F(比賽通過)
如果知道中位數的話 我們就可以n2logn暴力地知道答案 剩下就是怎么找中位數的問題
找中位數可以用權值線段樹來做 注意單個點權值占一半以上的情況
總復雜度3*T*n2logn/2
G
無視
H(比賽通過)
水題
I(比賽通過)
樹分治中點分治的一個小部分 變成帶權的了 直接dfs一次即可
J(比賽通過)
結論題
很容易可以知道前三個我們肯定是可以確認是原數列的前三個
因為A0=0 A0+A1 A0+A2這三個肯定是最小的
比如樣例0 1 2 2 我們可以先確認前三個0 1 2
則這三個產生的數列是1 2 3 接下來我們看與目標數列1 2 2 3 3 4對比最小的缺什么
很容易發現缺了個2 所以我們必須補個2
因為數列是非遞減的Ai與之前數相加產生的數不大於Ai+1與之前數相加所產生的數
這樣繼續check直到數列被填滿
寫的話就是直接暴力找 看起來復雜度會爆炸 但其實中間很多就直接break相當於剪枝了 能過
K
題意:
你要玩一個猜數游戲 答案為X 你最多只能問N次 問的時間最多不能超過V
第i次詢問你可以選一個數Y猜 如果Y大於X的話 會花費Ai的時間 不然的話會花費Ai+Bi的時間
你只有每次猜完后才可以猜下一次 問你最后可以從1開始猜到的最大區間長度為多少
解:
dp dp[i][j]表示只使用詢問 i到n 而且時間不超過j所能知道的答案