CSP-S模拟18
再次模拟退役,最近心态又双叒叕有点炸。。。。
实力确实也真不行
A. 最长反链
猜结论,从大到小能选就选,然后打表发现能选与不能选有明显的分界,于是直接二分答案
然后因为判断时候需要 \(*10\) 炸 \(int\) 了
我有大病吧,一共几个变量还不开 \(long long\)
code
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
int t, l, r;
bool check(ll x){
ll j = 1; while(j <= x)j *= 10;
return ((j + x) > r) && (x * 10 > r);
}
void solve(){
l = read(), r = read();
int nl = l, nr = r, pos = nl - 1;
while(nl <= nr){
int mid = (nl + nr) >> 1;
if(check(mid))nr = mid - 1;
else pos = mid, nl = mid + 1;
}
printf("%d\n",r - pos);
}
int main(){
t = read();
for(int ask = 1; ask <= t; ++ask)solve();
return 0;
}
B. 2A+x
比较,,,,失败
想偏了,一直以为跟二进制有关系。。。。
考虑一次变化会让 \(k\) 成为 \([2k,2k +x ]\) 的数,那么每个数变成了区间 \([l,r]\)
维护所有的 \(l,r\) , 当前答案就是 \(max(0, max(l) - min(r))\)
然后每次让 \(min(r)\) 对应的区间变化即可
在题库上因为时限问题只能各种玄学随机?不过用懒惰删除的堆可以通过
code
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 100005;
int n, x, a[maxn];
typedef pair<ll, int> pli;
ll ans;
ll rl[maxn], rr[maxn];
set<pli> mir, mxl;
int main(){
n = read(), x = read();
for(int i = 1; i <= n; ++i)a[i] = read();
sort(a + 1, a + n + 1);
n = unique(a + 1, a + n + 1) - a - 1;
for(int i = 1; i <= n; ++i)mir.insert(pli(a[i], i)), mxl.insert(pli(a[i], i)), rl[i] = rr[i] = a[i];
ans = a[n] - a[1];
while(clock() <= CLOCKS_PER_SEC * 3.5){
int i = 0;
for(i = 1; i <= 100; ++i){
ll mr = (*(mir.begin())).first, id = (*(mir.begin())).second, ml = (*(--mxl.end())).first;
ans = min(ans, max(ml - mr, 0ll));
if(ans == 0 || mr >= 1e18)break;
mir.erase(pli(mr, id));
mxl.erase(pli(ml, id));
rl[id] = rl[id] * 2;
rr[id] = rr[id] * 2 + x;
mir.insert(pli(rr[id], id));
mxl.insert(pli(rl[id], id));
}
if(i != 101)break;
}
printf("%lld\n", ans);
return 0;
}
C. No Rest for the Wicked
考场暴力死因:重载了小于号还用小根堆,正正得负。
考虑 \(f(x, v)\) 表示当疫情值为 \(x\) 从 \(v\) 出发能够到达的最大位置
那么后面再扫到 \(v\) 并且新的疫情值小于等于 \(x\) 时,就没有再搜下去的必要了,直接取 $f(x, v) $ 即可
所以比较优秀的搜索顺序是按照疫情值从大到小考虑
当然他还是 \(n^2\)
继续考虑这个过程,如果我们钦定了疫情值的下界 \(x\) 呢,发现此时
\(max(c_i,c_j) <= x <= min(t_i, t_j)\) 的边可以随便走,那么 \(i ,j\) 就可以看做一个联通块
\(c_i <= x <= min(t_i, c_j - 1)\) 的边可以走到 \(i\) ,也可以从 \(i - > j\) ,但是不能从 \(j\) 走回 \(i\), 也就是说边是单向的
于是我们把 \(s, t\) 离散化,按照疫情值在对应区间加边,线段树分治,用可撤销并查集维护连通性以及答案
在扫描到叶子节点时更新 \(l == c_i\) 的答案
过程中我们先走右边,在处理左侧单向边时候直接把 \(i\) 在并查集里的权值改成 \(max(val_i, ans_j, val_j)\)
code
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<iostream>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 400005;
int n, m;
struct city{
int c, t, s, id;
friend bool operator < (const city &x, const city &y){
return x.c > y.c;
}
}d[maxn];
struct edge{int u, v;}e[maxn];
bool vis[maxn];
int f[maxn], size[maxn], val[maxn];
int ans[maxn];
int fa(int x){return f[x] == x ? x : fa(f[x]);}
void merge(int x, int y){
x = fa(x); y = fa(y);
if(x == y)return;
if(size[x] < size[y])swap(x, y);
size[x] += size[y];
f[y] = x;
val[x] = max(val[x], val[y]);
}
struct sta{
bool dir;
int fx, x, valfx, tim;
}st[maxn];
int top;
void add_edge(int tim, int id, bool dir){
if(dir){
int u = e[id].u, v = e[id].v;
u = fa(u), v = fa(v);
if(val[u] < ans[v]){
++top; st[top].tim = tim;
st[top].dir = dir;
st[top].x = st[top].fx = u;
st[top].valfx = val[u];
val[u] = ans[v];
}
}else{
int u = e[id].u, v = e[id].v;
u = fa(u), v = fa(v);
if(u == v)return;
if(size[u] < size[v])swap(u, v);
++top;
st[top].x = v;
st[top].fx = u;
st[top].valfx = val[u];
st[top].dir = dir;
st[top].tim = tim;
f[v] = u;
val[u] = max(val[u], val[v]);
size[u] += size[v];
}
}
void del(int tim){
while(top && st[top].tim == tim){
if(st[top].dir){
val[st[top].x] = st[top].valfx;
}else{
int u = st[top].fx, v = st[top].x;
size[u] -= size[v];
f[v] = v;
val[u] = st[top].valfx;
}
--top;
}
}
vector<int>sol[maxn + maxn];
struct seg{
struct node{vector<int> dir, nir;}t[maxn << 2 | 1];
void insert(int x, int l, int r, int L, int R, int id, bool dir){
if(L <= l && r <= R){
if(dir)t[x].dir.push_back(id);
else t[x].nir.push_back(id);
return;
}
int mid = (l + r) >> 1;
if(L <= mid)insert(x << 1, l, mid, L, R, id, dir);
if(R > mid)insert(x << 1 | 1, mid + 1, r, L, R, id, dir);
}
void solve(int x, int l, int r){
for(int v : t[x].dir)add_edge(x, v, 1);
for(int v : t[x].nir)add_edge(x, v, 0);
if(l == r){
for(int v : sol[l])ans[v] = max(ans[v], val[fa(v)]);
del(x);
return;
}
int mid = (l + r) >> 1;
solve(x << 1 | 1, mid + 1, r);
solve(x << 1, l, mid);
del(x);
}
}t;
int ls[maxn + maxn];
int main(){
n = read(), m = read();
for(int i = 1; i <= n; ++i)d[i].c = read(), d[i].t = read(), d[i].s = read();
for(int i = 1; i <= m; ++i)e[i].u = read(), e[i].v = read();
int cnt = 0;
for(int i = 1; i <= n; ++i)ls[++cnt] = d[i].c, ls[++cnt] = d[i].t;
sort(ls + 1, ls + cnt + 1); cnt = unique(ls + 1, ls + cnt + 1) - ls - 1;
for(int i = 1; i <= n; ++i)d[i].c = lower_bound(ls + 1, ls + cnt + 1, d[i].c) - ls;
for(int i = 1; i <= n; ++i)d[i].t = lower_bound(ls + 1, ls + cnt + 1, d[i].t) - ls;
for(int i = 1; i <= n; ++i)sol[d[i].c].push_back(i);
for(int i = 1; i <= m; ++i){
int &u = e[i].u, &v = e[i].v;
if(d[u].c > d[v].c)swap(u, v);
t.insert(1, 1, cnt, d[v].c, min(d[v].t, d[u].t), i, 0);
if(d[u].c <= min(d[v].c - 1, d[u].t))t.insert(1, 1, cnt, d[u].c, min(d[v].c - 1, d[u].t), i, 1);
}
for(int i = 1; i <= n; ++i)f[i] = i, size[i] = 1, val[i] = d[i].s, ans[i] = d[i].s;
t.solve(1, 1, cnt);
for(int i = 1; i <= n; ++i)printf("%d ",ans[i]);
return 0;
}
D. 暴力题
非常的寄,线段树搞个部分分还因为没开 \(long long\) 炸了。。。
空间,空间..........
把柿子变成 \((b_ii^k-(b_ii^k \mod w))/w\)
然后考虑用值域线段树分别维护 \(b_ii^k\) 和 \(b_ii^k \mod w\)
我们只考虑在当前区间内的排名,然后拼接两个儿子时候处理右儿子排名的变化
具体的,对于 \(b_ii^k\mod w\) 维护 \(cnt_{x , y}\) 表示 \([b_i \mod w == x ]\) \([i \mod w == y]\) 的数的个数
暴力转移即可
\(b_ii^k\) 维护 \(f(x) = \sum b_ii^x\)
那么 \(f(k)\) 即为所求
考虑拼接右儿子,那么 \(f(t) = \sum b_i (i + size_l)^t\)
展开 \(f(t) = \sum \sum_{x= 0}^{t}b_i i^x(^t_x)size_l^{t -x}\)
也就是 \(rson_{f(x)}(^t_x)size_l^{t -x}\)
于是直接转移即可
code
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define int long long
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 100005;
const int mod = 998244353;
const int mx = 1e5;
ll qpow(ll x, ll y, ll p){
ll ans = 1;
for(; y; y >>= 1, x = x * x % p)if(y & 1)ans = ans * x % p;
return ans;
}
int n, k, w, q;
ll a[maxn], ikw[maxn], ikm[maxn], c[6][6];
int root;
struct seg{
struct node{
int l, r, size, cnt[5][5], f[6];
}t[maxn * 70];
int cnt;
#define ls t[x].l
#define rs t[x].r
void push_up(int x){
t[x].size = t[ls].size + t[rs].size;
for(int i = 0; i < w; ++i)
for(int j = 0; j < w; ++j)
t[x].cnt[i][j] = t[ls].cnt[i][j];
for(int i = 0; i < w; ++i)
for(int j = 0; j < w; ++j)
t[x].cnt[i][(j + t[ls].size) % w] += t[rs].cnt[i][j];
for(int i = 0; i <= k; ++i)t[x].f[i] = t[ls].f[i];
for(int i = 0; i <= k; ++i){
int nans = 0;
for(int j = 0; j <= i; ++j){
nans = (nans + 1ll * t[rs].f[j] * c[i][j] % mod * qpow(t[ls].size, i - j, mod) % mod) % mod;
}
t[x].f[i] = (t[x].f[i] + nans) % mod;
}
}
void insert(int &x, int l, int r, int pos, int opt){
if(!x) x = ++cnt;
if(l == r){
if(opt){
++t[x].size;
++t[x].cnt[l % w][t[x].size % w];
for(int i = 0; i <= k; ++i)t[x].f[i] += 1ll * l * qpow(t[x].size, i, mod) % mod, t[x].f[i] %= mod;
}else{
--t[x].cnt[l % w][t[x].size % w];
for(int i = 0; i <= k; ++i)t[x].f[i] -= 1ll * l * qpow(t[x].size, i, mod) % mod, t[x].f[i] = (t[x].f[i] % mod + mod) % mod;
--t[x].size;
}
return;
}
int mid = (l + r) >> 1;
if(pos <= mid)insert(t[x].l, l, mid, pos, opt);
else insert(t[x].r, mid + 1, r, pos, opt);
push_up(x);
}
void print(){
ll ans = 0;
ans = t[root].f[k];
for(int i = 0; i < w; ++i)
for(int j = 0; j < w; ++j)
ans -= i * qpow(j, k, w) % w * t[root].cnt[i][j];
ans = (ans % mod + mod) % mod;
ans = ans * qpow(w, mod - 2, mod) % mod;
printf("%lld\n",ans);
}
}t;
#undef int
int main(){
n = read(), k = read(), w = read();
for(int i = 1; i <= n; ++i)a[i] = read();
for(int i = 1; i <= n; ++i)ikw[i] = qpow(i, k, w);
for(int i = 1; i <= n; ++i)ikm[i] = qpow(i, k, mod);
for(int i = 0; i <= k; ++i){
c[i][0] = 1;
for(int j = 1; j <= i; ++j)c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
}
q = read();
for(int i = 1; i <= n; ++i)t.insert(root, 0, mx, a[i], 1);
for(int i = 1; i <= q; ++i){
int pos = read(), x = read();
t.insert(root, 0, mx, a[pos], 0);
a[pos] = x;
t.insert(root, 0, mx, a[pos], 1);
t.print();
}
return 0;
}