大三第十一周学习笔记
创始人
2024-01-21 14:50:33

周一

赛前最后的努力了,加油!!

前几天疯狂写作业,现在还有三场比赛的题没补,加油!

L. Buy Figurines(堆+线段树)

这题的关键在于只有n个人,怎么利用这个保证复杂度

考虑维护这n个人的离开时间,用一个优先队列维护

那么对于当前状态,可以处理处当前每个队的人数,怎么快速求最值呢

这个其实就是一个可以修改的堆,可以用set或者线段树实现

#include
#define l(k) (k << 1)
#define r(k) (k << 1 | 1)
#define rep(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;typedef long long ll;
typedef pair pii;
const int N = 2e5 + 10;
pair b[N];
int t[N << 2], n, m;
ll last[N];void up(int k)
{t[k] = min(t[l(k)], t[r(k)]);
}void clear(int k, int l, int r)
{t[k] = 0;if(l == r) return;int m = l + r >> 1;clear(l(k), l, m);clear(r(k), m + 1, r);
}void modify(int k, int l, int r, int x, int p)
{if(l == r){t[k] += p;return;}int m = l + r >> 1;if(x <= m) modify(l(k), l, m, x, p);else modify(r(k), m + 1, r, x, p);up(k);
}int ask(int k, int l, int r)
{if(l == r) return l;int m = l + r >> 1;if(t[l(k)] <= t[r(k)]) return ask(l(k), l, m);else return ask(r(k), m + 1, r);
}int main()
{int T; scanf("%d", &T);while(T--){scanf("%d%d", &n, &m);_for(i, 1, n) scanf("%d%d", &b[i].first, &b[i].second);sort(b + 1, b + n + 1);_for(i, 1, m) last[i] = 0;clear(1, 1, m);ll ans = 0;priority_queue, greater> q;_for(i, 1, n){auto [a, s] = b[i];while(!q.empty() && a >= q.top().first){modify(1, 1, m, q.top().second, -1);q.pop();}int cur = ask(1, 1, m);if(a >= last[cur]) last[cur] = a + s;else last[cur] += s;ans = max(ans, last[cur]);modify(1, 1, m, cur, 1);q.push({last[cur], cur});}printf("%lld\n", ans);}return 0; 
} 

C. Slipper(建图+最短路)

每一层加一个点,这一层连到它,然后它连向d+k层的所有点,然后跑最短路即可

#include
#define rep(i, a, b) for(int i = (a); i < (b); i++) 
#define _for(i, a, b) for(int i = (a); i <= (b); i++) 
using namespace std;typedef long long ll;
const int N = 2e6 + 10;
ll d[N];
struct node
{int v; ll w;bool operator < (const node& rhs) const{return w > rhs.w;}
};
vector> g[N];
vector ve[N];
int n, k, p, s, t, cnt;void dfs(int u, int fa, int dep)
{ve[dep].push_back(u);for(auto [v, w]: g[u]){if(v == fa) continue;dfs(v, u, dep + 1);}
}void solve()
{_for(i, 1, 2 * n) d[i] = 1e18;d[s] = 0;priority_queue q;q.push({s, d[s]});while(!q.empty()){node x = q.top(); q.pop();int u = x.v;if(d[u] != x.w) continue;for(auto [v, w]: g[u])if(d[v] > d[u] + w){d[v] = d[u] + w;q.push({v, d[v]});}}
}int main()
{int T; scanf("%d", &T);while(T--){scanf("%d", &n);_for(i, 1, 2 * n) g[i].clear(), ve[i].clear();_for(i, 1, n - 1){int u, v, w;scanf("%d%d%d", &u, &v, &w);g[u].push_back({v, w});g[v].push_back({u, w});}scanf("%d%d%d%d", &k, &p, &s, &t);dfs(1, 0, 1);cnt = n;_for(i, 1, n){if(!ve[i].size()) break;cnt++;for(int x: ve[i]) g[x].push_back({cnt, 0}); for(int x: ve[i + k]) g[cnt].push_back({x, p});if(i - k >= 1) for(int x: ve[i - k]) g[cnt].push_back({x, p});}solve();printf("%lld\n", d[t]);}return 0; 
} 

J. Eat, Sleep, Repeat(博弈)

这是一类博弈题,就是说操作次数其实是一定的,直接算操作次数的奇偶即可,关键是怎么算。

对于这题,可以发现限制为0把数分成了很多段。

每一段内,每个数尽可能的小,从最小的数开始往上。

#include
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;typedef long long ll;
const int N = 1e5 + 10;
map mp;
int n, k;
ll a[N];ll cal(vector ve, int st)   //函数这里不要忘记开long long
{if(!ve.size()) return 0;ll sum = 0;for(auto x: ve) sum += x - st;int pos = st - 1;while(mp.count(pos + 1) && mp[pos + 1]) pos++;if(pos == st - 1) return sum;ll res = 0, num = ve.size();_for(i, st, pos) {if(num <= mp[i]){res += 1LL * (i - st) * num;   //相乘的时候注意开1LLnum = 0;break;}else{res += 1LL * (i - st) * mp[i];num -= mp[i];}}res += num * (pos + 1 - st);return sum - res;
}int main()
{int T; scanf("%d", &T);while(T--){mp.clear();scanf("%d%d", &n, &k);_for(i, 1, n) scanf("%lld", &a[i]);sort(a + 1, a + n + 1);vector ve;while(k--){int x, y;scanf("%d%d", &x, &y);mp[x] = y;if(!y) ve.push_back(x);}ve.push_back(-1);ve.push_back(2e9);sort(ve.begin(), ve.end());ll ans = 0;rep(i, 0, ve.size() - 1){int l = lower_bound(a + 1, a + n + 1, ve[i]) - a;int r = lower_bound(a + 1, a + n + 1, ve[i + 1]) - a - 1;vector v;_for(j, l, r) v.push_back(a[j]);ans += cal(v, ve[i] + 1);}puts(ans % 2 == 1 ? "Pico" : "FuuFuu");}return 0;
}

G. Grade 2(打表/异或)

比赛时是推出结论的

其实如果打表找一下规律,能出的更快

首先异或与加法联系紧密

对于A^x对A的改变为正负x

可以这么理解,异或的作用就是为1的地方取反,那么最多就是全部把0变1,也就是加x,相反最小就是减x。

范围宽放一点话就是二进制位

那么kx^x=kx+a

gcd(kx ^ x, x) = gcd(kx + a, x) = gcd(a, x)

也就是说其实gcd取决于异或后改变了多少

那么题目给的x是1e6,这个非常关键,意味着不会改变太多

再考虑二进制位,会发现如果k的范围可以遍历二进制位的所有数,那么一定会有重复,那么重复就可以想到循环节

考虑t为大于x的最小的二次幂数

那么kx = (k + t) x  (mod t)

因为改变的就是低位的二进制,所以只用看低位二进制的,那么发现t是循环 节

也就是说,gcd(kx ^ x, x)以t为循环节

那么l和r转化为前缀和

对于当前,看有几个t,多出来的部分用前缀和

#include
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;typedef long long ll;
const int N = 2e6 + 10;
ll s[N], mod;ll gcd(ll a, ll b) { return !b ? a: gcd(b, a % b); }ll cal(ll r)
{ll k = r / mod;return k * s[mod] + s[r - k * mod];
}int main()
{ll x, n;scanf("%lld%lld", &x, &n);ll t = 1;while(t <= x) t <<= 1;mod = t;_for(i, 1, mod) s[i] = s[i - 1] + (gcd(i * x ^ x, x) == 1);while(n--){ll l, r;scanf("%lld%lld", &l, &r);printf("%lld\n", cal(r) - cal(l - 1));}return 0;
}

I. Dragon Bloodline(二分答案+贪心)

这题的重点是贪心

首先有一个结论,就是对于当前最高的2次幂,如果把它分配给大于等于它的,那么一定不会更差。因为在一个合法解里面,可以进行替换。

那么2的次幂从高到低遍历,首先把大于等于它的都分配给它,如果用完了就下一个,否则把多出来的给前cnt大的,这样肯定是浪费最少的。

二分答案的上界需要思考一下,要小心check的时候爆long long

分母部分为b的和,分子部分为maxa,这是上界。这样子check的时候不会爆long long

#include
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;typedef long long ll;
const int N = 5e4 + 10;
int a[N], b[25], cnt[25], n, k;
ll c[N];bool check(ll key)
{_for(i, 1, n) c[i] = a[i] * key;_for(i, 1, k) cnt[i] = b[i];for(int j = k; j >= 1; j--){_for(i, 1, n){ll cur = min(c[i] / (1 << (j - 1)), (ll)cnt[j]);cnt[j] -= cur;c[i] -= cur * (1 << (j - 1));if(!cnt[j]) break;}if(cnt[j]){cnt[j] = min(cnt[j], n);nth_element(c + 1, c + cnt[j], c + n + 1, greater());_for(i, 1, cnt[j]) c[i] = 0;}}_for(i, 1, n)if(c[i])return false;return true;
}int main()
{int T; scanf("%d", &T);while(T--){scanf("%d%d", &n, &k);_for(i, 1, n) scanf("%d", &a[i]);_for(i, 1, k) scanf("%d", &b[i]);int mx = 0;_for(i, 1, n) mx = max(mx, a[i]);ll l = 0, r = 2e15 / mx;while(l + 1 < r){ll m = l + r >> 1;if(check(m)) l = m;else r = m;}printf("%lld\n", l);} return 0;
}

C. Magic(差分约束)

推一下三个不等式即可

注意每个位置非负有Si - Si-1 >= 0

这道题是求一个变量的最值,那么建立一个超级源点,对所有点连边权为0的边,然后从这个点开始spfa。注意不要漏掉0

养成好习惯,打完代码后不急着编译,从头肉眼查错,重点看有没有变量名打错

#include
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;const int N = 1e4 + 10;
int d[N], vis[N], cnt[N], S, n, k;
vector> g[N];bool spfa()
{_for(i, 0, n + 1) d[i] = -1e9, vis[i] = cnt[i] = 0;  //最长路,有负权边,初始化为负无穷d[S] = 0;deque q;q.push_back(S);while(!q.empty()){int u = q.front(); q.pop_front();vis[u] = 0;for(auto [v, w]: g[u])if(d[v] < d[u] + w)   //注意这里是最长路{d[v] = d[u] + w;if(!vis[v]){if(!q.empty() && d[v] > d[q.front()]) q.push_front(v);  //注意判空else q.push_back(v);vis[v] = 1;if(++cnt[v] > n) return false;}}}return true;
}int main()
{int T; scanf("%d", &T);while(T--){scanf("%d%d", &n, &k);_for(i, 0, n + 1) g[i].clear();S = n + 1;_for(i, 1, n){int p; scanf("%d", &p);g[max(i - k, 0)].push_back({min(i + k - 1, n), p});g[i - 1].push_back({i, 0});}_for(i, 0, n) g[S].push_back({i, 0});int q; scanf("%d", &q);_for(i, 1, q){int l, r, b;scanf("%d%d%d", &l, &r, &b);g[r].push_back({l - 1, -b});}if(!spfa()) puts("-1");else printf("%d\n", d[n]);} return 0;
}

Link with Level Editor II(矩阵+线段树)

首先图比较小,可以用矩阵乘法。两个矩阵相乘就是恰好就是计算方案数,于是可以用矩阵来维护。

容易发现可以双指针求,关键是如何迅速求一段的值

可以直接用线段树求,但是会T

因为只关心1到m的答案,所以可以用一个向量不断右乘矩阵,这也可以优化掉一个m

继续养成习惯,写完检查一遍,防止变量。

#include
#define l(k) (k << 1)
#define r(k) (k << 1 | 1)
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;typedef long long ll;
const int N = 5e3 + 10;
struct martix 
{ ll s[25][25]; martix() { memset(s, 0, sizeof s); }
}t[N << 2];
int n, m, k;
ll v[25];martix mul(martix A, martix B)
{martix C;_for(i, 1, m)_for(j, 1, m)_for(k, 1, m)C.s[i][j] += A.s[i][k] * B.s[k][j];return C;
}void mul(martix A)
{ll res[25] = {0};_for(i, 1, m)_for(j, 1, m)res[i] += v[j] * A.s[j][i];_for(i, 1, m) v[i] = res[i];
}void up(int k)
{t[k] = mul(t[l(k)], t[r(k)]);
}void build(int k, int l, int r)
{if(l == r){int l; scanf("%d", &l);memset(t[k].s, 0, sizeof t[k].s);_for(i, 1, m) t[k].s[i][i] = 1;while(l--){int u, v;scanf("%d%d", &u, &v);t[k].s[u][v] = 1;}return;}int m = l + r >> 1;build(l(k), l, m);build(r(k), m + 1, r);up(k);
}void ask(int k, int l, int r, int L, int R)
{if(L <= l && r <= R){mul(t[k]);return;}int m = l + r >> 1;if(L <= m) ask(l(k), l, m, L, R);if(R > m) ask(r(k), m + 1, r, L, R);
}bool check(int l, int r)
{_for(i, 1, m) v[i] = 0;v[1] = 1;ask(1, 1, n, l, r);return v[m] <= k;
}int main()
{int T; scanf("%d", &T);while(T--){scanf("%d%d%d", &n, &m, &k);build(1, 1, n);int r = 0, ans = 0;_for(l, 1, n){while(r + 1 <= n && check(l, r + 1)) r++;ans = max(ans, r - l + 1);}printf("%d\n", ans);} return 0;
}

还有一种骚操作可以避免删除。

在l和r中加一个mid,每次计算拆成[l, mid] 乘上 [mid + 1, r]

右半部分r拓展的时候维护

左半部分用一个bi维护i到mid的值

当l超过mid的时候,mid=r,然后计算bi。

这样依然是O(n)的,同时避免了删除操作

#include
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;typedef long long ll;
const int N = 5e3 + 10;
struct martix 
{ ll s[25][25]; martix() { memset(s, 0, sizeof s); }
}a[N], b[N];
int n, m, k;martix mul(martix A, martix B)
{martix C;_for(i, 1, m)_for(j, 1, m)_for(k, 1, m)C.s[i][j] += A.s[i][k] * B.s[k][j];return C;
}void init(martix& A)  //初始化为单位矩阵 注意引用
{memset(A.s, 0, sizeof A.s);_for(i, 1, m) A.s[i][i] = 1;
}int main()
{int T; scanf("%d", &T);while(T--){scanf("%d%d%d", &n, &m, &k);_for(i, 1, n){init(a[i]);int l; scanf("%d", &l);while(l--){int u, v;scanf("%d%d", &u, &v);a[i].s[u][v] = 1;}}int l = 1, mid = 1, r = 1, ans = 0;martix cur; init(cur);b[1] = a[1];_for(l, 1, n){if(l > mid){mid = r;init(cur);martix t; init(t);for(int i = mid; i >= l; i--){t = mul(a[i], t);b[i] = t;}}while(r + 1 <= n && mul(b[l], mul(cur, a[r + 1])).s[1][m] <= k) cur = mul(cur, a[++r]);ans = max(ans, r - l + 1);}printf("%d\n", ans);} return 0;
}

周二

Map(数学)

由边成比例写出两个圆的方程,相减得到过两个交点的直线,然后直线和圆联立得到一元二次方程,然后求根公式,得到两个解,判断一下交点是否在矩形内即可。

需要推一些公式,比较复杂

#include
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;const double EPS = 1e-8;struct Point {double x, y;Point (double x = 0, double y = 0) : x(x), y(y) {}
};
int sgn(double x) {return (x < -EPS) ? -1 : (x > EPS);
}double Cross(Point a, Point b) {return a.x * b.y - a.y * b.x;
}
double Cross(Point a, Point b, Point c) {return Cross(Point(b.x - a.x, b.y - a.y), Point(c.x - a.x, c.y - b.y));
}double x[10], y[10];bool Judge(Point p, Point a, Point b, Point c, Point d) {return sgn(Cross(Point(b.x - a.x, b.y - a.y), Point(p.x - a.x, p.y - a.y)) * Cross(Point(d.x - c.x, d.y - c.y), Point(p.x - c.x, p.y - c.y))) == -1;
}bool check(double x1, double y1)
{return (Judge(Point(x1, y1), Point(x[5], y[5]), Point(x[6], y[6]), Point(x[8], y[8]), Point(x[7], y[7])) &&Judge(Point(x1, y1), Point(x[5], y[5]), Point(x[8], y[8]), Point(x[6], y[6]), Point(x[7], y[7])));
}int main()
{int T; scanf("%d", &T);while(T--){_for(i, 1, 8) scanf("%lf%lf", &x[i], &y[i]);//计算kdouble k = (pow(x[1] - x[2], 2) + pow(y[1] - y[2], 2)) / (pow(x[5] - x[6], 2) + pow(y[5] - y[6], 2));//直线方程double A = 2 * (k * x[5] - k * x[6] - x[1] + x[2]);double B = 2 * (k * y[5] - k * y[6] - y[1] + y[2]);double C = (x[1] * x[1] + y[1] * y[1] - x[2] * x[2] - y[2] * y[2] - k * (x[5] * x[5] + y[5] * y[5] - x[6] * x[6] - y[6] * y[6]));//圆方程double a = 1 - k;double b = 2 * (k * x[5] - x[1]);double c = 1 - k;double d = 2 * (k * y[5] - y[1]);double e = x[1] * x[1] + y[1] * y[1] - k * x[5] * x[5] - k * y[5] * y[5];//一元二次方程if(sgn(B)){double D = -A / B;double E = -C / B;double ta = a + c * D * D;double tb = b + 2 * c * D * E + d * D;double tc = c * E * E + d * E + e;//解方程double x1 = (-tb + sqrt(tb * tb - 4 * ta * tc)) / (2 * ta);double y1 = D * x1 + E;double x2 = (-tb - sqrt(tb * tb - 4 * ta * tc)) / (2 * ta);double y2 = D * x2 + E;if(check(x1, y1)) printf("%.10lf %.10lf\n", x1, y1);else printf("%.10lf %.10lf\n", x2, y2);}else{double X = -C / A;e += a * X * X + b * X;double ta = c, tb = d, tc = e;double y1 = (-tb + sqrt(tb * tb - 4 * ta * tc)) / (2 * ta);double y2 = (-tb - sqrt(tb * tb - 4 * ta * tc)) / (2 * ta);if(check(X, y1)) printf("%.10lf %.10lf\n", X, y1);else printf("%.10lf %.10lf\n", X, y2);}}return 0;
}

Maex(树形dp)

0是一个很重要的点,如果当前子树没有0,那么值就是全0

设dp[u]为以u为根子树中bi的最大和,也就是说以u为根的子树时的答案

那么u的b值为siz[u],对于它的儿子v,只有一个儿子的子树是有0的,其他都没有0

没有0的子树答案是0,所以要选一个答案最大的子树

即dp[u] = siz[u] + max(dp[v])

#include
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;typedef long long ll;
const int N = 5e5 + 10;
vector g[N];
int siz[N], n;
ll dp[N];void dfs(int u, int fa)
{siz[u] = 1;ll mx = 0;for(int v: g[u]){if(v == fa) continue;dfs(v, u);siz[u] += siz[v];mx = max(mx, dp[v]);}dp[u] = siz[u] + mx;
}int main()
{int T; scanf("%d", &T);while(T--){scanf("%d", &n);_for(i, 1, n) g[i].clear();_for(i, 1, n - 1){int u, v;scanf("%d%d", &u, &v);g[u].push_back(v);g[v].push_back(u);}dfs(1, 0);printf("%lld\n", dp[1]);}return 0;
}

Loop(思维)

贪心考虑每一次操作

把第一个大于前一个数的位置找出,为i

然后把ai-1提出来,插到后面去,插到第一个比它小的数前面

考虑如何快速求

可以一次性处理,把所有提出来的数放一起,由于插是随意的,所以排个序,然后把这些数和原来剩下的数做一个归并即可

#include
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;const int N = 3e5 + 10;
int st[N], top, n, k;int main()
{int T; scanf("%d", &T);while(T--){top = 0;scanf("%d%d", &n, &k);priority_queue q;_for(i, 1, n){int x; scanf("%d", &x);while(k && top && x > st[top]){k--;q.push(st[top--]);}st[++top] = x;}_for(i, 1, top){while(!q.empty() && q.top() > st[i]){printf("%d ", q.top());q.pop();}printf("%d ", st[i]);}while(!q.empty()) {printf("%d ", q.top());q.pop();}puts("");}return 0;
}

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
1和0什么意思网络用语 0和1... 最近,有网友给大家出了份考卷,卷面是几位“00后”的QQ对 话:dbq、xswl、nss、nbcs、...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
七大洲八大洋 七大洲简易图三角... 一、北美洲(中北美和加勒比海)北美洲面积约2471万平方公里,居世界第三。人口约5.79亿(2016...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...