文章目录
中超联赛
CSL中超可没巨佬们训练强度大
“什么是BSGS、莫队、KD tree”
我看我是完全不懂噢
HDU6950 Mod, Or and Everything
题意
求 ( n m o d 1 ) ∣ ( n m o d 2 ) ∣ . . . ∣ ( n m o d ( n − 1 ) ) (n\ mod\ 1)|(n\ mod \ 2)|...|(n\ mod\ (n-1)) (n mod 1)∣(n mod 2)∣...∣(n mod (n−1))的值
分析
打比赛时,
队友:经过化简可以得到答案为 2 k − 1 2^k-1 2k−1
我:观察样例可以得到答案为 2 k − 1 2^k-1 2k−1
QAQ
而实际上std的思路: n m o d i ≤ ⌈ n 2 ⌉ − 1 n\ mod\ i \leq \lceil \frac{n}{2}\rceil-1 n mod i≤⌈2n⌉−1,所以 n m o d ( n − i ) = i n\ mod\ (n-i)=i n mod (n−i)=i
所以这个和值就是 0 0 0到 ⌈ n 2 ⌉ − 1 \lceil \frac{n}{2}\rceil-1 ⌈2n⌉−1的所有整数的和. 求出来是 2 k − 1 2^k-1 2k−1,这里的 k k k是某个带 n n n的式子,懒得认真推的话可以根据样例猜出,是令 n = 2 x n=2^x n=2x,这个 x x x向下取整再减1得到 k k k. 但如果 x x x本来就是整数,那么 x x x还要再减去1.(总之看代码吧,或者去看std代码)
代码
#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 n-1 n−1个节点的带权完全图,节点标记为 2 , 3 , . . . , n 2,3,...,n 2,3,...,n. 完全图中从点 i i i到点 j j j的边的权值大小为 l c m ( i , j ) lcm(i,j) lcm(i,j),请你求出这个图的最小生成树。 n ≤ 10000000 n\leq 10000000 n≤10000000.
分析
容易知道,对任何一个边 ( i , j ) (i,j) (i,j),这个边的权值不会小于 m a x ( i , j ) max(i,j) max(i,j).
那么对于一个合数,它连向自己的因子即可,这时候边的权值就是这个合数。由于因子一定比自己小,所以在 2 , 3 , . . i 2,3,..i 2,3,..i中一定存在。
而对于一个质数 x x x,和任意一点 i i i相连,都有 l c m ( x , i ) = x ⋅ i lcm(x,i)=x·i lcm(x,i)=x⋅i. 故令 i i i最小,取2即可保证边权最小。
分析完毕。找出质数,然后分类处理即可。线性筛处理之
其他
首先看到题:哇, 1 e 8 1e8 1e8的范围,这怎么做。这算法都给限制到 O ( n ) O(n) O(n)了,难不成是有什么公式?
思考一会之后:(其实已经得到正解)用线性筛试一波,自己调试1.5秒,完蛋
尝试各种卡常之后:优化到1.1秒了,实在顶不住了,要不直接交?
交上去之后:卧槽,MLE,那我不求 M S T MST MST前缀和了吧,直接算,但是这样更容易超时了,纠结
再交:居然700多ms过了,杭电评测机强啊
打完比赛后,才发现,原来数据规模看错了,是 1 e 7 1e7 1e7,根本不需要卡()
代码
#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 n n点 m m m边的图( n ≤ 100000 n\leq100000 n≤100000, m ≤ 500000 m\leq500000 m≤500000),你需要将其分割成恰好 K K K组,满足:
- 每组中任取两个点 p , q p,q p,q,他们之间的路径中,最长边不大于 D D D
- 不同组中任取两个点 p , q p,q p,q,他们之间的路径中,所有边都大于 D D D
现在给定图和 K K K,问你是否找得到一个满足条件的 D D D,如果找得到,输出最小的 D D D. 否则输出 − 1 -1 −1.
分析
容易知道,相同权值的边要么不在任何组内部,要么全都在同一组点里面。假如相同权值的边,有的在组内,有的在组外的话,这个 D D D值肯定只会更大不会更小。
那么贪心思路显而易见了,将所有边按权值升序排序,使用并查集,从左往右,将所有权值相同的边的点合并。然后统计并查集数量,更新答案。也就是在保证“相同权值的边要么不在任何组内部,要么全都在同一组点里面”这个性质的前提下寻找能否分为 K K K组。而要使 D D D尽可能小,应让处于组内的边尽可能小,所以使用升序。
代码
#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 ) O(n^2) O(n2)了)
第一次接触到悬线法,代码可能相对繁琐
#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 k k. 如果有多个,找出左端点最左的
分析
先转化一下。异或运算里,任意 x x x的逆元是 x x x本身,故对于前缀和 p r e [ i ] pre[i] pre[i], p r e [ j ] pre[j] pre[j], i i i到 j j j的异或和可以表示为 p r e [ i ] pre[i] pre[i]^ p r e [ j ] pre[j] pre[j].
这之后,直接存异或和,问题转化为:找到两个相距最近的 i , j i,j i,j,其异或和不小于 k k k.
枚举右端点,然后可能的左端点用 T r i e Trie Trie树维护,从左到右找,每次找到一个可能的编号就覆盖一下,这样能保证最后找到的是最右边的。不过也可以直接保存最右边的节点。
以后千万不要看着std调试代码,调着调着就调得跟std一模一样了
不过说白了也是之前从来没用过trie吧orz,根本不熟
代码
/**
* @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;
}