和12差70分,模拟赛21不知道挂了多少,模拟赛24挂了80,事实证明挂一场考试没什么事,挂两场我就不行啦
由于正在专心改T1的时候老姚催总结,所以口糊了几行上去
总结
和第12差70分,昨天上午挂了80分。。。
这个阶段开局28名,本来可以追上来,结果因为用vector越界然后RE了,就止步于15名了。
挂一场其实没啥,挂两场就不行了,还是太菜了
问题就是改题效率太低,不会的一定要抓紧时间看题解搞懂
晚间测试13
阶段排名 15
暴踩std!!!
A Dove 打扑克
-
nmlog搞了65,然后就去看T2了,和正解好像只差一个set
-
比正解还多了个树状数组。
-
set里维护都有那些集合大小出现过,然后枚举就好,直接双指针前缀和,就不用树状数组了。
Code
#include <set>
#include <cstdio>
#define abs(a) ({int xx = a; xx < 0 ? -xx : xx;})
const int N = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
std::set<int> s;
std::set<int>::iterator it1, it2;
int n, m, f[N], siz[N], b[N], size;
long long ans;
int Find(int x) {
return x == f[x] ? x : (f[x] = Find(f[x]));
}
int main() {
freopen("cards.in", "r", stdin);
freopen("cards.out", "w", stdout);
n = read(); m = read();
for (int i = 1; i <= n; ++i)
f[i] = i, siz[i] = 1;
size = b[1] = n;
s.insert(1);
while (m--) {
int od = read();
if (od == 1) {
int x = Find(read()), y = Find(read());
if (x == y) continue;
if (!--b[siz[x]]) s.erase(siz[x]);
if (!--b[siz[y]]) s.erase(siz[y]);
f[x] = y; siz[y] += siz[x];
siz[x] = 0;
if (!b[siz[y]]++) s.insert(siz[y]);
size--;
}
else {
int x = read(), sum = 0; ans = 0;
if (!x) {
printf("%lld
", 1LL * size * (size - 1) / 2);
continue;
}
it1 = it2 = s.begin();
for (; it2 != s.end(); ++it2) {
while (it1 != s.end() && *it1 + x <= *it2) sum += b[*it1++];
/*if (*it1 + x <= *it2) */ans += 1LL * sum * b[*it2];
}
printf("%lld
", ans);
}
}
return 0;
}
B Cicada 与排序 (Unaccepted)
- 神奇的题,瞎搞了10分。
联赛模拟测试24
阶段排名 15
本来就是靠T3拉分,结果它挂了80~90分,和第12差距70分,看来要回归最初的美好了。
A 最大或
- 就从前往后扫到一位不同的,后面就全是1就行了,挺好想的。
Code
#include <cstdio>
#define int long long
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
signed main() {
freopen("maxor.in", "r", stdin);
freopen("maxor.out", "w", stdout);
int T = read();
while (T--) {
int l = read(), r = read(), s = 0;
for (int i = 1LL << 59, g = 0; i; i >>= 1) {
if (!(l & i) && (r & i)) g = 1;
if (g || ((l & i) && (r & i))) s += i;
}
printf("%lld
", s);
}
return 0;
}
B 答题
-
写的50分暴力只给了30分,出题人为了卡分连数据范围也不遵守了,我也是无fuck说了
-
要知道的是题目的意思就是让求 n 个数组成的 (2^n) 个数第 (left lfloor p*(2^n) ight floor) 小的数
-
正解是meet in the middle,先把前n/2个数组成的数记录下来,后面的数记录下来,二分第k小的数是多少,双指针查询就可以了
Code
#include <cmath>
#include <cstdio>
#include <algorithm>
typedef long long ll;
const int N = 1.1e6;
int n, a[45], f1[N], f2[N], s1, s2, n2, sum, l, r;
double p;
ll k;
void Dfs1(int x, int s) {
if (x > n2) return f1[++s1] = s, void();
Dfs1(x + 1, s); Dfs1(x + 1, s + a[x]);
}
void Dfs2(int x, int s) {
if (x > n) return f2[++s2] = s, void();
Dfs2(x + 1, s); Dfs2(x + 1, s + a[x]);
}
bool Judge(int x, ll s = 0) {
for (int i = 1, j = s2; i <= s1; ++i) {
for (; j >= 1 && f1[i] + f2[j] > x; --j);
if ((s += j) >= k) return 1;
}
return 0;
}
int main() {
freopen("answer.in", "r", stdin);
freopen("answer.out", "w", stdout);
scanf("%d%lf", &n, &p);
n2 = n / 2;
k = ceil(p * (1LL << n));
for (int i = 1; i <= n; ++i)
scanf("%d
", &a[i]), r += a[i];
Dfs1(1, 0); Dfs2(n2 + 1, 0);
std::sort(f1 + 1, f1 + s1 + 1);
std::sort(f2 + 1, f2 + s2 + 1);
while (l < r) {
int mid = l + r >> 1;
if (Judge(mid)) r = mid;
else l = mid + 1;
}
printf("%d
", l);
return 0;
}
C 联合权值·改
-
主要是求和的时候会被卡,剩下的都可过,可是for循环的时候v写的<=vecotr的size,然后就RE 5分,挂了80~90分
-
就是一个三元环的问题,学会了n根号n不重复的枚举三元环就解决了,我连hash判联通都想到了,我和正解也就差个这个,加上就90了,
-
90是因为本来时限1000的题让某队改成了200,到我交的时候改成了100,然后我有两个点99ms,使劲卡常A掉了
Code
#include <cstdio>
#include <vector>
#include <algorithm>
#define re register
typedef long long ll;
const int N = 3e4 + 5, M = 59999;
int read(re int x = 0, re int f = 1, re char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
std::vector<int> t[N], e[N];
struct Hash {
int next, x, y;
}h[N<<1];
int head[N<<1], hac;
void Add(int x, int y) {
t[x].push_back(y);
int ha = (x * N + y) % M;
h[++hac] = (Hash) {head[ha], x, y};
head[ha] = hac;
}
bool Find(int x, int y) {
int ha = (x * N + y) % M;
for (re int i = head[ha]; i; i = h[i].next)
if (h[i].x == x && h[i].y == y)
return 1;
return 0;
}
int n, m, a[N], od, in[N], mx;
long long s;
bool Cmp(const int &x, const int &y) {
return a[x] > a[y];
}
int main() {
freopen("link.in", "r", stdin);
freopen("link.out", "w", stdout);
n = read(), m = read(); od = read();
while (m--) {
int x = read(), y = read();
Add(x, y); Add(y, x);
in[x]++, in[y]++;
}
for (re int i = 1; i <= n; ++i)
a[i] = read();
for (re int x = 1; x <= n; ++x) {
for (re int i = 0; i < t[x].size(); ++i) {
int y = t[x][i];
if (in[y] > in[x] || (in[y] == in[x] && y > x))
e[x].push_back(y);
}
}
for (re int x = 1; x <= n; ++x) {
if (t[x].size() < 2) continue;
if (od != 2) {
std::sort(t[x].begin(), t[x].end(), Cmp);
int w = a[t[x][0]], w2 = 0;
for (re int i = 1; i < t[x].size(); ++i)
if (!Find(t[x][0], t[x][i]))
w2 = a[t[x][i]], i = t[x].size();
if ((w *= w2) > mx) mx = w;
}
if (od != 1) {
ll s1 = 0, s2 = 0;
for (re int i = 0; i < t[x].size(); ++i)
s1 += a[t[x][i]], s2 += a[t[x][i]] * a[t[x][i]];
s += s1 * s1 - s2;
for (re int i = 0; i < e[x].size(); ++i) {
int y = e[x][i];
for (re int j = 0; j < e[y].size(); ++j) {
int z = e[y][j];
if (!Find(x, z)) continue;
s -= 2LL * a[x] * a[y] + 2LL * a[x] * a[z] + 2LL * a[y] * a[z];
}
}
}
}
printf("%d
%lld
", mx, s);
return 0;
}
D 你相信引力吗
-
不会写,就写了个30分n2暴力。
-
首先可以想到一个冰锥会和两边不降的冰锥,直到第一个高于自己的冰锥构成危险冰锥对
-
求危险冰锥对数,那就只计算前面的冰锥,那显然维护一个单调不升的单调栈就可以了
-
题目中还提到是环,考虑不可能跨过最高点造成贡献,就直接从最高点断开计算
-
还有重复的情况,就另开一个数组记录之前与当前高度相同的数量
Code
#include <cstdio>
const int N = 5e6 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
int n, mx, a[N<<1], stk[N], s[N], tp;
long long ans;
int main() {
freopen("jolyne.in", "r", stdin);
freopen("jolyne.out", "w", stdout);
n = read();
for (int i = 1; i <= n; ++i) {
a[i] = a[i+n] = read();
if (a[i] > a[mx]) mx = i;
}
stk[tp=1] = a[mx];
s[1] = 1;
for (int i = mx + 1; i <= mx + n - 1; ++i) {
for (; tp && stk[tp] < a[i]; --tp) ans++;
if (stk[tp] == a[i]) ans += s[tp] + (a[i] != a[mx]);
else ans++;
stk[++tp] = a[i];
s[tp] = a[i] == stk[tp-1] ? s[tp-1] + 1 : 1;
}
for (; tp > 2 && stk[tp] != stk[2]; --tp) ans++;
printf("%lld
", ans);
return 0;
}
联赛模拟测试23
阶段排名 15
A 数列
-
一眼就是扩展欧几里得,可是忘了咋算,就yy了个逆元求出x,y的一组解,可是找不到绝对值和最小的解,就拿了30分
-
在a<b的情况求出特解后让x尽可能接近0即可
B 数对
-
本来打的n3的,拿了0分,考完发现理解错题了,上次就是什么数对理解错了,这次又理解错了,信息奥赛考数学就算了吧,还考语文?强烈谴责出题人这种描述不清题目的行为!!
-
先对序列按a+b的值排序在进行DP
-
首先考虑n2的DP,f[i][j]表示考虑前i个元素,a的最大值为j的最大价值
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
f[i][j] = std::max(f[i][j], f[i-1][j]);
if (a[i].b >= j) f[i][std::max(j, a[i].a)] = std::max(f[i][std::max(j, a[i].a)], f[i-1][j] + a[i].w);
ans = std::max(ans, f[i][j]);
}
}
-
这样只有60分。考虑优化。
-
可以每次发现需要修改的值不多,每次只有f[i][a[i]]会被修改最大值,还有f[i][a[i]]~f[i][b[i]]的值会加上w[i],、
-
就写一颗支持最大值查询,区间加,单点修改的线段树即可
Code
#include <cstdio>
#include <algorithm>
#define ls (rt << 1)
#define rs (rt << 1 | 1)
typedef long long ll;
const int N = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
struct Node {
int a, b, w;
bool operator < (const Node &y) const {
return a + b < y.a + y.b;
}
}a[N];
int n, b[N<<1], m;
ll t[N<<3], tag[N<<3];
void Pushdown(int rt) {
t[ls] += tag[rt];
t[rs] += tag[rt];
tag[ls] += tag[rt];
tag[rs] += tag[rt];
tag[rt] = 0;
}
ll Ask(int rt, int l, int r, int x, int y) {
if (x > r || y < l) return 0;
if (x <= l && r <= y) return t[rt];
if (tag[rt]) Pushdown(rt);
int mid = l + r >> 1;
return std::max(Ask(ls, l, mid, x, y), Ask(rs, mid + 1, r, x, y));
}
void Add(int rt, int l, int r, int x, int y, int w) {
if (x > r || y < l) return;
if (x <= l && r <= y) return t[rt] += w, tag[rt] += w, void();
if (tag[rt]) Pushdown(rt);
int mid = l + r >> 1;
Add(ls, l, mid, x, y, w);
Add(rs, mid + 1, r, x, y, w);
t[rt] = std::max(t[ls], t[rs]);
}
void Change(int rt, int l, int r, int x, ll w) {
if (l == r) return t[rt] = std::max(t[rt], w), void();
if (tag[rt]) Pushdown(rt);
int mid = l + r >> 1;
if (x <= mid) Change(ls, l, mid, x, w);
else Change(rs, mid + 1, r, x, w);
t[rt] = std::max(t[ls], t[rs]);
}
int main() {
freopen("pair.in", "r", stdin);
freopen("pair.out", "w", stdout);
n = read();
for (int i = 1; i <= n; ++i) {
a[i].a = b[++m] = read();
a[i].b = b[++m] = read();
a[i].w = read();
}
std::sort(b + 1, b + m + 1);
m = std::unique(b + 1, b + m + 1) - b - 1;
for (int i = 1; i <= n; ++i) {
a[i].a = std::lower_bound(b + 1, b + m + 1, a[i].a) - b;
a[i].b = std::lower_bound(b + 1, b + m + 1, a[i].b) - b;
}
std::sort(a + 1, a + n + 1);
for (int i = 1; i <= n; ++i) {
ll mx = Ask(1, 1, m, 1, std::min(a[i].a, a[i].b));
Add(1, 1, m, a[i].a, a[i].b, a[i].w);
Change(1, 1, m, a[i].a, mx + a[i].w);
}
printf("%lld
", t[1]);
return 0;
}
C 最小距离
-
我们的姚队实在是钛聚辣!!我再次用暴力碾过了正解,我跑三千多毫秒,有好几个正解跑了四、五千毫秒
-
一眼就是原题,除了跑多源最短路没想到,其他的都还有些印象,然后就只能对每个特殊点都跑一遍dij了,没想到A了,交到原来的题上只有15分。
-
以所有特殊点为起点跑多源最短路,并记录每个点是由那个点更新的,然后枚举每条边,如果两端点的起点不一样,就更新这两个起点的答案。
Code
#include <queue>
#include <cstdio>
#include <cstring>
typedef long long ll;
const int N = 2e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
struct Edge {
int next, t, d;
}e[N<<1];
int head[N], edc;
void Add(int x, int y, int z) {
e[++edc] = (Edge) {head[x], y, z};
head[x] = edc;
}
std::priority_queue< std::pair<ll, int> > q;
int n, m, p, a[N], c[N];
ll d[N], ans[N];
bool v[N];
int main() {
freopen("mindis.in", "r", stdin);
freopen("mindis.out", "w", stdout);
n = read(), m = read(); p = read();
memset(d, 0x3f, 4 * n + 4);
memset(ans, 0x3f, 4 * n + 4);
for (int i = 1; i <= p; ++i) {
a[i] = read();
c[a[i]] = a[i];
q.push(std::make_pair(d[a[i]] = 0, a[i]));
}
while (m--) {
int x = read(), y = read(), z = read();
Add(x, y, z); Add(y, x, z);
}
while (!q.empty()) {
int x = q.top().second; q.pop();
if (v[x]) continue;
v[x] = 1;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].t;
if (d[y] > d[x] + e[i].d) {
d[y] = d[x] + e[i].d;
c[y] = c[x];
q.push(std::make_pair(-d[y], y));
}
}
}
for (int i = 1; i <= edc; i += 2) {
int x = e[i].t, y = e[i+1].t;
ll dis = d[x] + d[y] + e[i].d;
x = c[x], y = c[y];
if (x == y) continue;
ans[x] = std::min(ans[x], dis);
ans[y] = std::min(ans[y], dis);
}
for (int i = 1; i <= p; ++i)
printf("%lld ", ans[a[i]]);
return 0;
}
D 真相 (Unaccepted)
- 由于T1肝了2小时,导致我实在是没时间了,就rand了一下,拿了0分
联赛模拟测试22
阶段排名23
A 求和
-
式子很显然,就是等差数列求和,主要是会爆longlong,就得用点别的方法
-
公式里有除以2,逆元不是很好算,所以就改成先除后龟速乘。
B 分组配对
-
本来想二分一下,算了算复杂度(O(n^2log^2n)),就写了个n方暴力,拿了60分
-
很显然的两个贪心,组内的最大实力值是大的乘大的,小的乘小的,然后能放进组里就放,放不进就开了新组
-
用倍增优化二分,先判断组长为(2^1,2^2,...)是否满足,到(2^x)时不满足时,就在(2^{x-1}~2^x)区间内二分长度(我二分的是右端点)
Code
Show Code
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N = 5e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
int n, ans, a[N], b[N], sa[N], sb[N];
long long m;
bool Judge(int l, int r) {
memcpy(sa + l, a + l, 4 * (r - l + 1));
memcpy(sb + l, b + l, 4 * (r - l + 1));
std::sort(sa + l, sa + r + 1);
std::sort(sb + l, sb + r + 1);
long long s = 0;
for (int i = l; i <= r; ++i)
if ((s += 1LL * sa[i] * sb[i]) > m)
return 0;
return 1;
}
int main() {
freopen("pair.in", "r", stdin);
freopen("pair.out", "w", stdout);
n = read();
scanf("%lld", &m);
for (int i = 1; i <= n; ++i)
a[i] = read();
for (int i = 1; i <= n; ++i)
b[i] = read();
int x = 1;
while (x <= n) {
int i, l = 0, r;
for (i = 2; x + i - 1 <= n; i <<= 1) {
if (Judge(x, x + i - 1)) continue;
l = x + i / 2 - 1, r = x + i - 1;
break;
}
if (!l) l = x + i / 2 - 1, r = n;
while (l < r) {
int mid = l + r + 1 >> 1;
if (Judge(x, mid)) l = mid;
else r = mid - 1;
}
x = l + 1;
ans++;
}
printf("%d
", ans);
return 0;
}
C 城市游戏 (Unaccepted)
- 看错题了,只拿了15分
D 简单计算
-
考场上没仔细想,拿了50分就走了。‘
-
正解就是考虑向下取整会比原来少多少
1LL * (n + 1) * m - n + Gcd(n, m) >> 1
联赛模拟测试21
阶段排名 28
A 小盆友的游戏
-
T1就是期望题,直接puts("0"),虽然给了5个样例但还是没发毛线关系
-
正解是构造函数(f_x=2^x-1),设整个局面的函数值为所有人函数值之和
-
发现每猜一次拳局面的函数值的变化量都是1
- 所以猜拳的次数就是最终局面的函数值与开始局面的函数值的差
B 花
-
写的暴力实在是太暴力了,于是它T成0分了
-
定义f[i][0/1]前i个位置的方案数,0表示还没有出现连续3个的情况,1相反。转移方程
C 表格 (Unaccepted)
- 开始YY了一个50分写法,结果跑的比暴力还慢,最后就只有10分,不过整个机房最高分也就是10分了
D 格式化
-
显然需要贪心排序,由于实在是太菜了,就没想出来,写了50分部分分然后用random_shuffle拿了15分。
-
先放使容量增加的,再放容量不变的,最后放使容量减小的
-
对于容量增加的,先放格式化之前小的
-
对于容量减小的,先放格式化之后大的