中超联赛

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;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
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;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
using namespace std;
const int maxn = 1e7 + 10; // 比赛的时候看成1e8,导致多了一次MLE
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$组,满足:

  1. 每组中任取两个点$p,q$,他们之间的路径中,最长边不大于$D$
  2. 不同组中任取两个点$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)
// #define int long long
const int inf = 0x3f3f3f3f;
const double dinf = 1e100;
typedef long long ll;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
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; // 并查集数量,初始为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)
// #define int long long
const int inf = 0x3f3f3f3f;
const double dinf = 1e100;
typedef long long ll;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
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
/**
* @file :vsDebug.cpp
* @brief :
* @date :2021-07-21
* @Motto :Love Sakurai Yamauchi Forever
*/
#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;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
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; // init
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; // 从上往下第j位
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;
}