因為本人參加的是洛谷的同步賽,可能題目順序與正式賽選手不同?
今年題目難度普遍偏低。只有 D,F 還好。
有些題就不粘代碼了。
A
按題目給的公式計算即可。注意應在最后的答案中去掉小數部分。
B
按照題意模擬即可。注意答案要與 \(0\) 取 \(\max\) 。
C
按照題意模擬即可。注意應該先做乘法在做除法,或者把后面的值用 double 或 float 數據類型的變量存儲,並在最后去掉小數部分。
時間復雜度 \(\mathcal O(n)\)。
D
算法一
設 \(x \oplus y = p\) ,則 \(x \oplus p = y\)。因此我們要找有多少 \(p\) 滿足 \(x \oplus p < x\) 。
考慮到 \(x \oplus y\) 的值最大為 \(2^{21} - 1\) ,因此我們用線性篩篩出 \(\le 2.1 \times 10^6\) 的所有素數,然后把它們插入一個 01Trie 。
然后我們要求的答案在 01Trie 中其實是一個個字數內的數的個數的和。
在遍歷 \(x\) 的每一個二進制位的過程中,設該二進制位為 \(c\),當前節點編號為 \(u\),\(tr_{u,0/1}\) 表示下一個分別是 \(0,1\) 的位置的編號。
如果 \(c=1\),那個 \(tr_{u,0}\) 內的所有子樹一定合法,我們加上它子樹內的數的個數,然后讓 \(u \to rt_{u,1}\),即進入右子樹。
如果 \(c=0\),那個子樹都不能造成貢獻,我們直接進入 \(tr_{u,0}\),即左子樹。
如果某個非起始位置 \(u=0\) ,直接退出即可。
時間復雜度為 \(\mathcal O((Cnt + m) \log V)\),其中 \(Cnt\) 是質數個數,\(V\) 是值域。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2.1e6 + 10;
int T, x;
int ans[MAXN];
int prim[MAXN], Cnt = 0;
bool vis[MAXN], flag[MAXN];
void Init() {
int M = 2100000;
for(int i = 2; i <= M; ++i) {
if(!vis[i]) prim[++Cnt] = i;
for(int j = 1; j <= Cnt && i * prim[j] <= M; ++j) {
vis[i * prim[j]] = true;
if(i % prim[j] == 0) break;
}
}
}
namespace Trie {
int tr[MAXN << 2][2], node_num = 0, siz[MAXN << 2];
void Insert(int x) {
int u = 0;
siz[0] ++;
for(int j = 21; j >= 0; --j) {
int c = (x >> j) & 1;
if(!tr[u][c]) tr[u][c] = ++node_num;
u = tr[u][c];
siz[u] ++;
}
}
int Query(int x) {
int u = 0, res = 0;
for(int j = 21; j >= 0; --j) {
int c = (x >> j) & 1;
if(c) {
res = res + siz[tr[u][c]];
u = tr[u][c ^ 1];
} else {
u = tr[u][c];
}
if(!u) return res;
}
return res;
}
}
int main() {
Init();
for(int i = 1; i <= Cnt; ++i) {
Trie::Insert(prim[i]);
}
scanf("%d", &T);
while(T--) {
scanf("%d", &x);
int res = Trie::Query(x);
printf("%d\n", res);
}
return 0;
}
算法二
考慮上面那個問題的本質。
其實問題可以再次轉化成有多少 \(p\) 滿足:如果設 \(x\) 與 \(p\) 第一個不同的位置 \(i\), \(x\) 在 \(i\) 這一位上為 \(1\)。
進一步分析可以發現,如果 \(x\) 的第 \(i\) 位為 \(1\),那么所有 \(2^{i} \le p < 2^{i+1}\) 的質數都是合法的。
這其實和 \(p\) 的最高位數有關,按照這個預處理個數即可。
總復雜度 \(\mathcal O((Cnt + m) \log V)\)。
#include<bits/stdc++.h> // 這個代碼雖然沒交但應該沒有問題
using namespace std;
const int MAXN = 2.1e6 + 10;
int T, x;
int ans[MAXN], a[200];
int prim[MAXN], Cnt = 0;
bool vis[MAXN], flag[MAXN];
int read() {
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
void Init() {
int M = 2100000;
for(int i = 2; i <= M; ++i) {
if(!vis[i]) prim[++Cnt] = i;
for(int j = 1; j <= Cnt && i * prim[j] <= M; ++j) {
vis[i * prim[j]] = true;
if(i % prim[j] == 0) break;
}
}
}
int main() {
Init();
for(int i = 1; i <= Cnt; ++i) {
int x = prim[i];
int lst, cnt = 0;
while(x) {
if(x & 1) lst = cnt;
x >>= 1, ++ cnt;
}
a[lst] ++;
}
cin >> T;
while(T--) {
int Ans = 0;
cin >> x;
for(int i = 0; i <= 21; ++i)
if(x & (1 << i)) Ans += a[i];
printf("%d\n", Ans);
}
return 0;
}
E
題意可以轉化成有 \(k\) 個可重集,每次向里面插入 \(p\) 個數對 \((a,b)\) ,每個數對 \(a,b\) 表示向第 \(a\) 個集合中插入一個值為 \(b\) 的數。
查詢的話就是第 \(x\) 個集合中在 \([y_{\min},y_{\max}]\) 內的數有多少個。
發現 \(k,n\) 都很小,對於每個集合維護一個樹狀數組即可。
時間復雜度 \(\mathcal O(n \log n)\)。
#include<bits/stdc++.h>
using namespace std;
int n, k, M = 1000;
struct Bit {
int sum[1010];
int lb(int x) { return x & -x; }
void Modify(int x, int k) { for(; x <= M; x += lb(x)) sum[x] += k; }
int Query(int x) { int res = 0; for(; x; x -= lb(x)) res += sum[x]; return res; }
}bit[1010];
int main() {
cin >> n >> k;
for(int i = 1, opt, p; i <= n; ++i) {
cin >> opt >> p;
if(opt == 1) {
for(int j = 1, x, y; j <= p; ++j) {
cin >> x >> y;
bit[x].Modify(y, 1);
}
} else {
int amax, amin;
cin >> amin >> amax;
int res = bit[p].Query(amax) - bit[p].Query(amin - 1);
cout << res << "\n";
}
}
}
F
考慮利用 \(dfs\) 序轉化到序列上。
考慮把深度當成主席樹的值域維護,每個位置對應深度權值為 \(1\)。
操作一可以直接用一個 lst 變量記錄。
操作二就可以直接查詢 \([dfn_x, dfn_x + siz_x - 1]\) 這段區間中 \(dep_{pre_i} \ge lst\) 的位置有多少。
注意特判一下一開始整個樹都是綠的。
時間復雜度 \(\mathcal O(n \log n)\) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 10;
struct edge {
int to, nxt;
}e[MAXN << 1];
int head[MAXN], num_edge = 1;
int n, m;
int root[MAXN];
int siz[MAXN], dfn[MAXN], pre[MAXN], dep[MAXN], Cnt = 0;
namespace Hjt {
#define ls lson[now_]
#define rs rson[now_]
int node_num = 0, siz[MAXN << 5], lson[MAXN << 5], rson[MAXN << 5];
void Insert(int &now_, int pre_, int l, int r, int pos, int k) {
now_ = ++node_num;
siz[now_] = siz[pre_] + k;
ls = lson[pre_], rs = rson[pre_];
if(l == r) return ;
int mid = (l + r) >> 1;
if(mid >= pos) Insert(ls, lson[pre_], l, mid, pos, k);
else Insert(rs, rson[pre_], mid + 1, r, pos, k);
}
int Query(int now_, int pre_, int L, int R, int l, int r) {
if(L <= l && r <= R) return siz[now_] - siz[pre_];
int mid = (l + r) >> 1, ans = 0;
if(mid >= L) ans += Query(ls, lson[pre_], L, R, l, mid);
if(mid < R) ans += Query(rs, rson[pre_], L, R, mid + 1, r);
return ans;
}
}
void add_edge(int from, int to) { e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; }
void dfs(int u, int fa) {
dep[u] = dep[fa] + 1;
dfn[u] = ++Cnt, pre[Cnt] = u, siz[u] = 1;
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
dfs(v, u);
siz[u] += siz[v];
}
}
int main() {
cin >> n >> m;
for(int i = 1, u, v; i < n; ++i) {
cin >> u >> v;
add_edge(u, v), add_edge(v, u);
}
dfs(1, 0);
for(int i = 1; i <= n; ++i) {
Hjt::Insert(root[i], root[i - 1], 1, n + 1, dep[pre[i]], 1);
}
for(int i = 1, opt, x, lst = n + 1; i <= m; ++i) {
cin >> opt >> x;
if(opt == 1) lst = x;
else {
int res = Hjt::Query(root[dfn[x] + siz[x] - 1], root[dfn[x] - 1], lst, n + 1, 1, n + 1);
cout << res << "\n";
}
}
return 0;
}
G
注意題目要求是第 \(x,y\) 個質數。
其實是一道詐騙題。
發現如果兩個數滿足相異或為 \(1\) 那么他們兩個數的值必定只差 \(1\),而這樣的質數對只有 \((2,3)\) 這一對,特判一下即可。
時間復雜度 \(\mathcal O(T)\) 。
