中超联赛
CSL中超可没巨佬们训练强度大
“什么是BSGS、莫队、KD tree”
我看我是完全不懂噢
HDU6950 Mod, Or and Everything
题意
求$(n\ mod\ 1)|(n\ mod \ 2)|…|(n\ mod\ (n-1))$的值
分析
打比赛时,
队友:经过化简可以得到答案为$2^k-1$
我:观察样例可以得到答案为$2^k-1$
QAQ
而实际上std的思路:$n\ mod\ i \leq \lceil \frac{n}{2}\rceil-1$,所以$n\ mod\ (n-i)=i$
所以这个和值就是$0$到$\lceil \frac{n}{2}\rceil-1$的所有整数的和. 求出来是$2^k-1$,这里的$k$是某个带$n$的式子,懒得认真推的话可以根据样例猜出,是令$n=2^x$,这个$x$向下取整再减1得到$k$. 但如果$x$本来就是整数,那么$x$还要再减去1.(总之看代码吧,或者去看std代码)
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include <bits/stdc++.h> #define fors(i, a, b) for(int i = (a); i <= (b); ++i) #define lson k<<1 #define rson k<<1|1 #define pb push_back #define lowbit(x) ((x)&(-(x))) #define mem(a) memset(a, 0, sizeof(a)) #define DDLC_ESCAPE_PLAN_FAILED ios::sync_with_stdio(false), cin.tie(0) #define int long long const int inf = 0x3f3f3f3f; const double dinf = 1e100; typedef long long ll;
using namespace std;
signed main() { DDLC_ESCAPE_PLAN_FAILED; int t; cin >> t; while(t--) { int n; cin >> n; int x = 0, res = n; while(res){ res >>= 1LL; x++; } if(n == 1LL){ cout << 0 << endl; continue; } if(n == (1LL << (x - 1LL))) x--; cout << (1LL << (x - 1LL)) - 1LL << endl; } return 0; }
|
HDU6954 Minimum spanning tree
题意
有一个$n-1$个节点的带权完全图,节点标记为$2,3,…,n$. 完全图中从点$i$到点$j$的边的权值大小为$lcm(i,j)$,请你求出这个图的最小生成树。$n\leq 10000000$.
分析
容易知道,对任何一个边$(i,j)$,这个边的权值不会小于$max(i,j)$.
那么对于一个合数,它连向自己的因子即可,这时候边的权值就是这个合数。由于因子一定比自己小,所以在$2,3,..i$中一定存在。
而对于一个质数$x$,和任意一点$i$相连,都有$lcm(x,i)=x·i$. 故令$i$最小,取2即可保证边权最小。
分析完毕。找出质数,然后分类处理即可。线性筛处理之
其他
首先看到题:哇,$1e8$的范围,这怎么做。这算法都给限制到$O(n)$了,难不成是有什么公式?
思考一会之后:(其实已经得到正解)用线性筛试一波,自己调试1.5秒,完蛋
尝试各种卡常之后:优化到1.1秒了,实在顶不住了,要不直接交?
交上去之后:卧槽,MLE,那我不求$MST$前缀和了吧,直接算,但是这样更容易超时了,纠结
再交:居然700多ms过了,杭电评测机强啊
打完比赛后,才发现,原来数据规模看错了,是$1e7$,根本不需要卡()
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include <bits/stdc++.h> #define fors(i, a, b) for(int i = (a); i <= (b); ++i) #define lson k<<1 #define rson k<<1|1 #define pb push_back #define lowbit(x) ((x)&(-(x))) #define mem(a) memset(a, 0, sizeof(a)) #define DDLC_ESCAPE_PLAN_FAILED ios::sync_with_stdio(false), cin.tie(0) #define int long long const int inf = 0x3f3f3f3f; const double dinf = 1e100; typedef long long ll;
using namespace std; const int maxn = 1e7 + 10; bool p[maxn]; int pr[maxn]; int pre[maxn]; void Prime(){ int cnt = 0; mem(p); p[0] = p[1] = 1; for(int i = 2; i < maxn; ++i){ if(!p[i]) pr[cnt++] = i; for(int j = 0; j < cnt && pr[j] * i < maxn; ++j){ p[pr[j] * i] = 1; if(i % pr[j] == 0) break; } } pre[0] = pre[1] = pre[2] = 0; for(int i = 3; i < maxn; ++i){ if(!p[i]) pre[i] = pre[i - 1] + 2 * i; else pre[i] = pre[i - 1] + i; } } signed main() { DDLC_ESCAPE_PLAN_FAILED; int t; cin >> t; Prime(); while(t--) { int n; cin >> n; cout << pre[n] << endl; } return 0; }
|
HDU6958 KD-Graph
看到过题数不多以及题面比较奇怪就没做了,以后一定不能盲目看榜,以及耐心读懂题
题意
有一个$n$点$m$边的图($n\leq100000$, $m\leq500000$),你需要将其分割成恰好$K$组,满足:
- 每组中任取两个点$p,q$,他们之间的路径中,最长边不大于$D$
- 不同组中任取两个点$p,q$,他们之间的路径中,所有边都大于$D$
现在给定图和$K$,问你是否找得到一个满足条件的$D$,如果找得到,输出最小的$D$. 否则输出$-1$.
分析
容易知道,相同权值的边要么不在任何组内部,要么全都在同一组点里面。假如相同权值的边,有的在组内,有的在组外的话,这个$D$值肯定只会更大不会更小。
那么贪心思路显而易见了,将所有边按权值升序排序,使用并查集,从左往右,将所有权值相同的边的点合并。然后统计并查集数量,更新答案。也就是在保证“相同权值的边要么不在任何组内部,要么全都在同一组点里面”这个性质的前提下寻找能否分为$K$组。而要使$D$尽可能小,应让处于组内的边尽可能小,所以使用升序。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| #include <bits/stdc++.h> #define fors(i, a, b) for(int i = (a); i <= (b); ++i) #define lson k<<1 #define rson k<<1|1 #define pb push_back #define pii pair<int, int> #define lowbit(x) ((x)&(-(x))) #define mem(a) memset(a, 0, sizeof(a)) #define DDLC_ESCAPE_PLAN_FAILED ios::sync_with_stdio(false), cin.tie(0)
const int inf = 0x3f3f3f3f; const double dinf = 1e100; typedef long long ll;
using namespace std; const int maxn= 100000 + 5; int fa[maxn]; struct edge{ int l, r, val; bool operator < (const edge& b)const{ return val < b.val; } }; vector<edge> e; inline int find(int x) { if(fa[x] == x) return x; return fa[x] = find(fa[x]); } bool join(int x, int y) { int ans = 0; int fx = find(x), fy = find(y); if(fx == fy) return 0; fa[fy] = fx; return 1; } signed main() { DDLC_ESCAPE_PLAN_FAILED; int t; cin >> t; while(t--) { e.clear(); int n, m, k; cin >> n >> m >> k; fors(i, 1, n) fa[i] = i; int u, v, c; fors(i, 1, m){ cin >> u >> v >> c; e.pb({u, v, c}); } sort(e.begin(), e.end()); int now = n; bool flag = 0; int ans = 0; for(int i = 0; i < m; ++i){ if(i == 0 || e[i].val != e[i - 1].val){ if(now == k) break; } if(!join(e[i].l, e[i].r)) continue; now--; ans = e[i].val; } cout << (now == k ? ans : -1) << endl; } return 0; }
|
HDU6957 Maximal submatrix
题意
给出一个矩阵,要你找一个最大的子矩阵,要求这个子矩阵每一列从上往下都是单调不下降序列。输出最大子矩阵的面积
分析
悬线法,将每个不比上面的元素小的元素标记,然后用这些标记过的元素组成子矩阵。接下来使用悬线dp即可。需要注意的是,子矩阵的最上面一行是可以不被标记的。
代码
(比赛时自己凹的二维dp一直超时,应该是复杂度超过$O(n^2)$了)
第一次接触到悬线法,代码可能相对繁琐
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| #include <bits/stdc++.h> #define fors(i, a, b) for(int i = (a); i <= (b); ++i) #define lson k<<1 #define rson k<<1|1 #define pb push_back #define lowbit(x) ((x)&(-(x))) #define mem(a) memset(a, 0, sizeof(a)) #define DDLC_ESCAPE_PLAN_FAILED ios::sync_with_stdio(false), cin.tie(0)
const int inf = 0x3f3f3f3f; const double dinf = 1e100; typedef long long ll;
using namespace std; const int maxn = 2005; bool v[maxn][maxn]; int l[maxn][maxn]; int r[maxn][maxn]; int u[maxn][maxn]; signed main() { DDLC_ESCAPE_PLAN_FAILED; int t; cin >> t; while(t--) { mem(v), mem(l), mem(r), mem(u); int n, m; cin >> n >> m; fors(i, 1, n){ fors(j, 1, m){ cin >> u[i][j]; l[i][j] = j; r[i][j] = j; } } fors(i, 1, n){ fors(j, 1, m){ if(i == 1 || u[i][j] >= u[i - 1][j]){ v[i][j] = 1; } } } fors(i, 1, n){ fors(j, 2, m){ if(v[i][j] && v[i][j - 1]) l[i][j] = l[i][j - 1]; } for(int j = m - 1; j >= 1; --j){ if(v[i][j] && v[i][j + 1]) r[i][j] = r[i][j + 1]; } } mem(u); fors(i, 1, n){ fors(j, 1, m) u[i][j] = 1; } int ans = 0; fors(i, 1, n){ fors(j, 1, m){ if(v[i][j] && v[i - 1][j]){ l[i][j] = max(l[i - 1][j], l[i][j]); } if(v[i][j]) u[i][j] = u[i - 1][j] + 1; } for(int j = m; j >= 1; --j){ if(v[i][j] && v[i - 1][j]){ r[i][j] = min(r[i - 1][j], r[i][j]); } } } fors(i, 1, n){ fors(j, 1, m){ ans = max(ans, u[i][j] * (r[i][j] - l[i][j] + 1)); } } cout << ans << endl; } return 0; }
|
HDU6955 Xor sum
题意
给一个整数数组,你需要找到最短的区间,其按位异或和不小于$k$. 如果有多个,找出左端点最左的
分析
先转化一下。异或运算里,任意$x$的逆元是$x$本身,故对于前缀和$pre[i]$,$pre[j]$,$i$到$j$的异或和可以表示为$pre[i]$^$pre[j]$.
这之后,直接存异或和,问题转化为:找到两个相距最近的$i,j$,其异或和不小于$k$.
枚举右端点,然后可能的左端点用$Trie$树维护,从左到右找,每次找到一个可能的编号就覆盖一下,这样能保证最后找到的是最右边的。不过也可以直接保存最右边的节点。
以后千万不要看着std调试代码,调着调着就调得跟std一模一样了
不过说白了也是之前从来没用过trie吧orz,根本不熟
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
|
#include <bits/stdc++.h> #define fors(i, a, b) for(int i = (a); i <= (b); ++i) #define lson k<<1 #define rson k<<1|1 #define pb push_back #define lowbit(x) ((x)&(-(x))) #define mem(a) memset(a, 0, sizeof(a)) #define DDLC_ESCAPE_PLAN_FAILED ios::sync_with_stdio(false), cin.tie(0)
const int inf = 0x3f3f3f3f; const double dinf = 1e100; typedef long long ll;
using namespace std; const int maxn = 1e5 + 10; const int mnx = (1LL << 24) + 10; int p[mnx][2], res[mnx], a[maxn]; signed main() { DDLC_ESCAPE_PLAN_FAILED; int t; cin >> t; while(t--) { int n, k; cin >> n >> k; fors(i, 1, n){ cin >> a[i]; a[i] ^= a[i - 1]; } int l = -1, r = n, ans = 1; res[1] = -1; p[1][0] = p[1][1] = 0; fors(i, 0, n){ int x = 1; int tmp = -1; for(int j = 29; j >= 0; --j){ if(!x) break; int u = (a[i] >> j) & 1; if(!((k >> j) & 1)){ if(p[x][u ^ 1]){ tmp = max(tmp, res[p[x][u ^ 1]]); } x = p[x][u]; } else x = p[x][u ^ 1]; } if(x) tmp = max(tmp, res[x]); if(tmp >= 0 && i - tmp < r - l) l = tmp, r = i; x = 1; for(int j = 29; j >= 0; --j){ int u = (a[i] >> j) & 1; if(!p[x][u]){ ans++; p[x][u] = ans, res[ans] = -1; p[ans][0] = p[ans][1] = 0; } x = p[x][u]; res[x] = max(res[x], i); } } if(l >= 0) cout << l + 1 << ' ' << r << endl; else cout << -1 << endl; } return 0; }
|