GJOI 2024.4.20 总结
Morning:
T1 小鸟
Statement:
在一个
在坐标系第一象限里,左下角的点的坐标是
所以本题我们考虑的整个平面总共有
在整点
从
你最多可以开
但是有
Solution:
考场被卡常力(悲
先计算
注意到一个点至多被一条直线经过,因此分别计算每一条直线的贡献,取最大贡献的
考虑一条经过
那么现在加入
注意
#include<bits/stdc++.h> #pragma GCC optimize(2) #define int long long using namespace std; const int N = 4e6 + 10, M = 5e3 + 10; int n, m, k; int phi[N], prime[N], tot; struct point{ int x, y; }p[M], s[N]; bool isprime[N]; map<int, int> mp; void init(){ for(int i = 1; i < N; i++) isprime[i] = true; phi[1] = 1; isprime[1] = false; for(int i = 2; i < N; i++){ if(isprime[i]) phi[i] = i - 1, prime[++tot] = i; for(int j = 1; j <= tot && i * prime[j] < N; j++){ isprime[i * prime[j]] = false; if(i % prime[j]) phi[i * prime[j]] = phi[i] * phi[prime[j]]; else{ phi[i * prime[j]] = phi[i] * prime[j]; break; } } } } bool cmp(struct point p1, struct point p2){ if(p1.x != p2.x) return p1.x > p2.x; return p1.y < p2.y; } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int T; cin >> T; init(); while(T--){ cin >> n >> k >> m; mp.clear(); mp[n - 1] += 2; int tmp = 0; for(int i = 1; i < n; i++) mp[(n - 1) / i] += phi[i] * (i == 1 ? 1 : 2); for(int i = 1; i <= m; i++){ cin >> p[i].x >> p[i].y; int g = __gcd(p[i].x, p[i].y); if((!p[i].x) || (!p[i].y)) g = 0; if(g) p[i].x /= g, p[i].y /= g; } sort(p + 1, p + m + 1, cmp); int ths = 1; // for(int i = 1; i <= m; i++) cout << p[i].x << " " << p[i].y << "\n"; for(int i = 2; i <= m; i++){ if((p[i].x != p[i - 1].x || p[i].y != p[i - 1].y) && (p[i].x || p[i - 1].x) && (p[i].y || p[i - 1].y)){ int g = max(p[i - 1].x, p[i - 1].y); // cout << p[i - 1].x << " " << p[i - 1].y << " " << g << " " << ths << "\n"; if(min(p[i - 1].x, p[i - 1].y) == 0) mp[n - 1]--, mp[n - 1 - ths]++; else mp[(n - 1) / g]--, mp[(n - 1) / g - ths]++; ths = 0; } ths++; } if(ths){ int g = max(p[m].x, p[m].y); if(min(p[m].x, p[m].y) == 0) mp[n - 1]--, mp[n - 1 - ths]++; else mp[(n - 1) / g]--, mp[(n - 1) / g - ths]++; } int ans = 0; for(map<int, int>::iterator it = mp.begin(); it != mp.end(); it++){ if(k <= 0) break; int val = (*it).first, cnt = (*it).second; if(cnt) s[++tmp] ={val, cnt}; } sort(s + 1, s + tmp + 1, cmp); for(int i = 1; i <= tmp; i++){ if(k <= 0) break; //cout << s[i].x << " " << s[i].y << "\n"; ans += s[i].x * min(k, s[i].y); k -= s[i].y; } cout << ans << "\n"; } return 0; } /* 1 9 1 7 8 2 0 1 7 6 6 8 3 3 0 5 3 4 */
T2 最长连续值域
Statement:
给出一个长度为
1
Solution:
考虑用莫队求解。
我们令
那么添加一个数
发现不好在删除的时候更新答案,于是使用回滚莫队。
#include<bits/stdc++.h> #define int long long #define bk(x) ((x + unit - 1) / unit) using namespace std; const int N = 5e4 + 10; int n, m, a[N]; int unit, st[N], ed[N]; struct query{ int l, r, id; }q[N]; int res, tmp, ans[N]; void clr(int x){st[x] = x + 1; ed[x] = x - 1;} void add(int x){ res = max(res, ed[x + 1] - st[x - 1] + 1); ed[st[x - 1]] = ed[x + 1]; st[ed[x + 1]] = st[x - 1]; } void del(int x){ ed[st[x - 1]] = x - 1; st[ed[x + 1]] = x + 1; } bool cmp(struct query q1, struct query q2){ if(bk(q1.l) != bk(q2.l)) return q1.l < q2.l; return q1.r < q2.r; } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> m; unit = sqrt(m); for(int i = 1; i <= n; i++) cin >> a[i]; for(int i = 1; i <= m; i++) cin >> q[i].l >> q[i].r, q[i].id = i; sort(q + 1, q + m + 1, cmp); int l = 1, r = 0; for(int i = 1; i <= m; i++){ if(bk(q[i].l) != bk(q[i - 1].l)){ for(int j = 0; j <= n + 1; j++) clr(j); r = min(n, bk(q[i].l) * unit); l = min(n, bk(q[i].l) * unit) + 1; res = 0; } if(bk(q[i].l) == bk(q[i].r)){ res = 0; for(int j = q[i].l; j <= q[i].r; j++) add(a[j]); ans[q[i].id] = res; for(int j = q[i].r; j >= q[i].l; --j) del(a[j]); res = 0; continue; } while(r < q[i].r) add(a[++r]); int tmp = res, _l = l; while(_l > q[i].l) add(a[--_l]); ans[q[i].id] = res; res = tmp; while(_l < l) del(a[_l++]); } for(int i = 1; i <= m; i++) cout << ans[i] << "\n"; return 0; }
T3 P5306 [COCI2018-2019#5] Transport
一道淀粉质板子题就不讲了。
Afternoon
T1 [ABC246G] Game on Tree 3
首先二分答案
于是这个时候我们判断
设
显然
判断
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 2e5 + 10; struct edge{ int v, next; }edges[N << 1]; int head[N], idx; void add_edge(int u, int v){ edges[++idx] = {v, head[u]}; head[u] = idx; } int n, a[N]; int f[N]; void dfs(int u, int fa){ int s = 0; for(int i = head[u]; i; i = edges[i].next){ int v = edges[i].v; if(v == fa) continue; dfs(v, u); s += f[v]; } f[u] += max(0ll, s - 1); } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n; for(int i = 2; i <= n; i++) cin >> a[i]; for(int i = 1; i < n; i++){ int x, y; cin >> x >> y; add_edge(x, y); add_edge(y, x); } int l = 1, r = 1e9, ans = 0; while(l <= r){ int mid = (l + r >> 1); for(int i = 1; i <= n; i++) f[i] = (a[i] >= mid); dfs(1, 0); if(f[1] == 0) r = mid - 1; else ans = mid, l = mid + 1; } cout << ans; return 0; }
T2 无向图染色
Statement:
给出
Solution:
考虑容斥。
定义不可行边为一条连接
对于一个孤立点,它染色的可能性就有
接下来我们考虑点数
-
该连通块有奇环:如果
为偶数,就只能把所有点染成 ,否则没有染色的可能。 -
该连通块没有奇环:注意到如果我们确定了一个点的颜色,整个连通块的颜色就可以确定了。那么一个点的可能性就是:
.
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 18 + 3, mod = 1e9 + 7; int n, m, k, z; int from[N], to[N], nodecnt; int qpow(int x, int y){ int ret = 1; while(y){ if(y & 1) ret = (ret * x) % mod; x = (x * x) % mod; y >>= 1; } return ret; } struct edge{ int v, next; }edges[N << 1]; int head[N], idx; void add_edge(int u, int v){ edges[++idx] = {v, head[u]}; head[u] = idx; } int col[N]; int popcnt(int x){if(!x) return 0; return popcnt(x >> 1) + (x & 1);} void build(int S){//建图 idx = 0; nodecnt = 0; for(int i = 1; i <= n; i++) head[i] = col[i] = 0; for(int i = 0; i < m; i++) if((1 << i) & S) add_edge(from[i + 1], to[i + 1]), add_edge(to[i + 1], from[i + 1]); for(int i = 1; i <= n; i++) if(head[i]) nodecnt++;//自由点的个数 } bool checker(int u){//检查是否有奇环 bool flag = true; for(int i = head[u]; i; i = edges[i].next){ int v = edges[i].v; if(!col[v]) col[v] = -col[u], flag &= checker(v); // else if(col[v] + col[u] != 0) flag = false; // } return flag; } int lsy(){ // if(k >= z - 1) return z - 1; if(2 * k < z) return 0; return 2 * k - z + 1; } int solver(){//算这个图染色的方案数 int ret = 1; for(int i = 1; i <= n; i++){ if((!col[i]) && head[i]){ col[i] = 1;//将第 1 个节点染成 1 if(checker(i) && (z <= 2 * k)) ret = (ret * lsy()) % mod; else if((z & 1) || z > 2 * k) ret = 0; } } return ret; } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> m >> k >> z; for(int i = 1; i <= m; i++) cin >> from[i] >> to[i]; int ans = 0; for(int S = 0; S < (1 << m); S++){ build(S); ans = (ans + ((qpow(k, n - nodecnt) * ((popcnt(S) & 1) ? -1 : 1) * solver()) % mod + mod) % mod) % mod; // cout << S << " " << nodecnt << " " << ans << "\n"; } cout << ans; return 0; }
本文作者:Little_corn
本文链接:https://www.cnblogs.com/little-corn/p/18152698
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步