Welcome to the NightCity~ Cyber|

XiaoLe_MC

园龄:1年2个月粉丝:3关注:9

[考试记录] 2024.9.16 csp-s模拟赛30

T1 不相邻集合

服了,考场上拉一泡权值线段树,硬是没调过来。然后一下考就知道是哪的问题了,服了~

维护两个东西:一是以 x 为右端点的最长可重集的长度,二是以 y 为右端点的最长可重集长度。两者一加再减一就是答案。然后考场上愣是想不到。这玩意用权值线段树很好维护,考虑到因为是以 x 为右端点的最长长度就是把以 1x2 中的最长长度加一即可。

#include<bits/stdc++.h>
using namespace std;
constexpr int B = 1 << 25;
char buf[B], *p1 = buf, *p2 = buf;
#define gt() (p1==p2 && (p2=(p1=buf)+fread(buf, 1, B, stdin), p1==p2) ? EOF : *p1++)
template <typename T> inline void rd(T &x){
x = 0; int f = 0; char ch = gt();
for(; !isdigit(ch); ch = gt()) f ^= ch == '-';
for(; isdigit(ch); ch = gt()) x = (x<<1) + (x<<3) + (ch^48);
x = f ? -x : x;
}
char obuf[B], *O = obuf;
#define pt(ch) (O-obuf==B && (fwrite(obuf, 1, B, stdout), O=obuf), *O++ = (ch))
template <typename T> inline void wt(T x){
if(x < 0) pt('-'), x = -x;
if(x >= 10) wt(x / 10); pt(x % 10 ^ 48);
}
#define fw fwrite(obuf, 1, O - obuf, stdout)
constexpr int N = 3e5 + 5, T = 1e6 + 5, M = 5e5 + 1;
int n, x, ans, mx, rt, as1, as2;
namespace ST{
int cnt, ls[T], rs[T], f[T], g[T];
inline void fadd(int &id, int l, int r, int x, int val){
if(!id) id = ++cnt;
if(l == r){
f[id] = max(f[id], val);
return;
}
int mid = (l + r) >> 1;
if(x <= mid) fadd(ls[id], l, mid, x, val);
else fadd(rs[id], mid+1, r, x, val);
f[id] = max(f[ls[id]], f[rs[id]]);
}
inline void gadd(int &id, int l, int r, int x, int val){
if(!id) id = ++cnt;
if(l == r){
g[id] = max(g[id], val);
return;
}
int mid = (l + r) >> 1;
if(x <= mid) gadd(ls[id], l, mid, x, val);
else gadd(rs[id], mid+1, r, x, val);
g[id] = max(g[ls[id]], g[rs[id]]);
}
inline int fquery(int &id, int l, int r, int x, int y){
if(!id || x > y) return 0;
if(x <= l && r <= y) return f[id];
int mid = (l + r) >> 1, ans = 0;
if(x <= mid) ans = max(ans, fquery(ls[id], l, mid, x, y));
if(y > mid) ans = max(ans, fquery(rs[id], mid+1, r, x, y));
return ans;
}
inline int gquery(int &id, int l, int r, int x, int y){
if(!id || x > y) return 0;
if(x <= l && r <= y) return g[id];
int mid = (l + r) >> 1, ans = 0;
if(x <= mid) ans = max(ans, gquery(ls[id], l, mid, x, y));
if(y > mid) ans = max(ans, gquery(rs[id], mid+1, r, x, y));
return ans;
}
}
bitset<M> vis;
int main(){
rd(n); while(n--){
rd(x); if(vis[x]){ wt(ans), pt(' '); continue; }
vis[x] = 1;
as1 = ST::fquery(rt, 1, M, 1, x-2) + 1;
ST::fadd(rt, 1, M, x, as1);
as2 = ST::gquery(rt, 1, M, x+2, M) + 1;
ST::gadd(rt, 1, M, x, as2);
ans = max(ans, as1 + as2 - 1);
wt(ans), pt(' ');
} return fw, 0;
}

T2 线段树

solution1

对于 n300 的部分,直接建树模拟即可。

solution 2

对于 n=2k 的部分。考虑遍历树的每一层,发现每一层树上节点的编号是有序的,那么找出 xy 包含的完整段编号即可用等差数列求和计算出贡献。对于第 i 层,起始编号为 a=2i1,块长为 b=2log2(n)i+1。那么 x 完全包含的块的编号为:

l={a+x1bbx1a+x1b+1bx1

同理 y 完全包含的块的编号为:

r={a+y1bbya+y1b1by

每一层完全包含的块构成等差数列,带公式即可。

solution 3

对于 x=1,y=n 的部分,机房大佬打表得出答案为 (n2)。但是由于 n 极大,实际上不好打(?打不了)。

正解

f(n,x) 表示有 n 个叶子,根节点编号为 x 的线段树的答案。显然有 f(n,x)=knx+bn。很好理解:当根节点的编号加上 p 时,儿子节点加上 2p,孙子加上 4p……显然一次函数,kn 只和树的形态有关。又因为:

f(n,x)=f(n2,2x)+f(n2,2x+1)+x

那就暴力拆狮子:

{kn=2kn2+2kn2+1bn=bn2+bn2+kn2

记搜即可。

#include<bits/stdc++.h>
using namespace std;
constexpr int B = 1 << 25;
char buf[B], *p1 = buf, *p2 = buf;
#define gt() (p1==p2 && (p2=(p1=buf)+fread(buf, 1, B, stdin), p1==p2) ? EOF : *p1++)
template <typename T> inline void rd(T &x){
x = 0; int f = 0; char ch = gt();
for(; !isdigit(ch); ch = gt()) f ^= ch == '-';
for(; isdigit(ch); ch = gt()) x = (x<<1) + (x<<3) + (ch^48);
x = f ? -x : x;
}
char obuf[B], *O = obuf;
#define pt(ch) (O-obuf==B && (fwrite(obuf, 1, B, stdout), O=obuf), *O++ = (ch))
template <typename T> inline void wt(T x){
if(x < 0) pt('-'), x = -x;
if(x >= 10) wt(x / 10); pt(x % 10 ^ 48);
}
#define fw fwrite(obuf, 1, O - obuf, stdout)
#define it __int128
#define int long long
#define ls (id << 1)
#define rs (id << 1 | 1)
constexpr int M = 1e9 + 7;
int T, n, x, y;
unordered_map<int, int> k, b;
inline int getk(int num){
if(k.count(num)) return k[num];
return k[num] = ((it)getk(num>>1) * 2 % M + (it)getk((num+1)>>1) * 2 % M + 1) % M;
}
inline int getb(int num){
if(b.count(num)) return b[num];
return b[num] = ((it)getb(num>>1) + (it)getk(num>>1) + (it)getb((num+1)>>1)) % M;
}
inline int work(int id, int num){
return ((it)getk(num) * id % M + (it)getb(num)) % M;
}
inline int getans(int id, int l, int r, int x, int y){
if(x <= l && r <= y) return work(id, r-l+1);
int mid = (l + r) >> 1, ans = 0;
if(x <= mid) ans = ((it)ans + (it)getans(ls, l, mid, x, y)) % M;
if(y > mid) ans = ((it)ans + (it)getans(rs, mid+1, r, x, y)) % M;
return ans;
}
signed main(){
k[1] = 1, b[1] = 0;
rd(T); while(T--){
rd(n), rd(x), rd(y);
wt(getans(1, 1, n, x, y)); pt('\n');
} return fw, 0;
}

T3 魔法师

昨天刚弄完拆绝对值的,今天弄拆最大值的。

默认法杖为 0, 咒语为 1。如果 a0+a1>b0+b1,那么有 a0b0>b1a1。所以,对每个物品维护一个价值 w

{w=abc=0w=bac=1

那么就可以把这个 w 放到权值线段树上去,同时对法杖和咒语分别维护一下区间最小的 ab。再稍微 pushup 一下即可。对于删除操作,使用 mutiset 维护线段树节点即可。注:卡空。

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 100, inf = 1e8, M = 25e4;
int Q, T, ans, rt;
namespace ST{
int cnt, ls[N<<1], rs[N<<1], l[N<<1], r[N<<1], num[N<<1], A[N<<1][2], B[N<<1][2];
multiset<int> ta[N][2], tb[N][2];
inline void pushup(int id){
A[id][0] = min(A[ls[id]][0], A[rs[id]][0]), A[id][1] = min(A[ls[id]][1], A[rs[id]][1]);
B[id][0] = min(B[ls[id]][0], B[rs[id]][0]), B[id][1] = min(B[ls[id]][1], B[rs[id]][1]);
num[id] = min({num[ls[id]], num[rs[id]], A[ls[id]][1] + A[rs[id]][0], B[ls[id]][0] + B[rs[id]][1]});
}
inline void build(int &id, int x, int y){
if(!id) id = ++cnt;
l[id] = x, r[id] = y, num[id] = inf;
A[id][0] = A[id][1] = B[id][0] = B[id][1] = inf;
if(x == y){
ta[x][0].insert(inf), ta[x][1].insert(inf);
tb[x][0].insert(inf), tb[x][1].insert(inf);
return;
} int mid = (x + y) >> 1;
build(ls[id], x, mid), build(rs[id], mid+1, y);
}
inline void add(int id, int ps, bool k, int a, int b){
if(l[id] == r[id]){
ta[ps][k].insert(a), tb[ps][k].insert(b);
num[id] = min((*ta[ps][0].begin()) + (*ta[ps][1].begin()), (*tb[ps][0].begin()) + (*tb[ps][1].begin()));
A[id][k] = *ta[ps][k].begin(); B[id][k] = *tb[ps][k].begin();
return;
} int mid = (l[id] + r[id]) >> 1;
add(((ps <= mid) ? ls[id] : rs[id]), ps, k, a, b);
pushup(id);
}
inline void del(int id, int ps, bool k, int a, int b){
if(l[id] == r[id]){
if(ta[ps][k].find(a) == ta[ps][k].end()) return;
ta[ps][k].erase(ta[ps][k].find(a)), tb[ps][k].erase(tb[ps][k].find(b));
num[id] = min((*ta[ps][0].begin()) + (*ta[ps][1].begin()), (*tb[ps][0].begin()) + (*tb[ps][1].begin()));
A[id][k] = *ta[ps][k].begin(); B[id][k] = *tb[ps][k].begin();
return;
} int mid = (l[id] + r[id]) >> 1;
del(((ps <= mid) ? ls[id] : rs[id]), ps, k, a, b);
pushup(id);
}
}
int main(){
// freopen("30.in", "r", stdin), freopen("out.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>Q>>T; ST::build(rt, 1, 5e5+1);
for(int i=1, opt, t, a, b; i<=Q; ++i){
cin>>opt>>t>>a>>b;
if(T) a ^= ans, b ^= ans;
if(opt != 2) ST::add(rt, (t ? b-a+M : a-b+M), t, a, b);
else ST::del(rt, (t ? b-a+M : a-b+M), t, a, b);
ans = ST::num[rt] >= inf ? 0 : ST::num[rt];
cout<<ans<<'\n';
} return 0;
}

T4 园艺

机房大佬用拐两次的方法骗分直接AC……

30pts

很典的区间DP,设 dp[l][r]dp[r][l] 分别表示拔完 lr 的草的价值并停留在 lrO(n2)

100pts

上面的dp无法继续优化,考虑换一种计算贡献的形式。

考虑这么一个事,如果从 k 点直接前往第 p 株草可以获得 dis(k,p) 的贡献,对于每一株草来说,这个贡献是最基础的,不能再少了。如果刚开始时从 k 向左走 m 步,那么在 k 右侧的草的贡献

#include<bits/stdc++.h>
using namespace std;
constexpr int B = 1 << 23;
char buf[B], *p1 = buf, *p2 = buf, obuf[B], *O = obuf;
#define gt() (p1==p2 && (p2=(p1=buf) + fread(buf, 1, B, stdin), p1==p2) ? EOF : *p1++)
template <typename T> inline void rd(T &x){
x = 0; int f = 0; char ch = gt();
for(; !isdigit(ch); ch = gt()) f ^= ch == '-';
for(; isdigit(ch); ch = gt()) x = (x<<1) + (x<<3) + (ch^48);
x = f ? -x : x;
}
#define pt(ch) (O-obuf==B && (fwrite(obuf, 1, B, stdout), O=obuf), *O++ = (ch))
template <typename T> inline void wt(T x){
if(x < 0) pt('-'), x = -x;
if(x >= 10) wt(x / 10); pt(x % 10 ^ 48);
}
#define fw fwrite(obuf, 1, O - obuf, stdout)
#define int long long
#define it __int128
#define p pair<int, int>
constexpr int N = 2e6 + 5;
int n, k, s[N], dp[N];
struct XiaoLe{
int tail, head; p st[N];
inline bool check(p a, p b, p c){
return ((it)(b.second - a.second) * (c.first - b.first) > (it)(c.second - b.second) * (b.first - a.first)) ? 1 : 0;
}
inline void push_back(p node){
while(head < tail && check(st[tail-1], st[tail], node)) --tail;
st[++tail] = node;
}
inline void push_front(p node){
while(head < tail && check(node, st[head], st[head+1])) ++head;
st[--head] = node;
}
inline p get_ltor(int k){
while(head < tail && (it)(st[tail].second - st[tail-1].second) > (it)k * (st[tail].first - st[tail-1].first)) --tail;
return st[tail];
}
inline p get_rtol(int k){
while(head < tail && (it)(st[head+1].second - st[head].second) < (it)k * (st[head+1].first - st[head].first)) ++head;
return st[head];
}
}xl[2]; // 0 : l->r 1 : r->l
signed main(){
rd(n), rd(k);
for(int i=2, a; i<=n; ++i) rd(a), s[i] = s[i-1] + a;
memset(dp, 0x7f, sizeof(int) * (n+1)); dp[k] = 0;
for(int i=1; i<=n; ++i) dp[k] += llabs(s[k] - s[i]);
int tot = n - 1, l = k, r = k;
xl[0].head = xl[1].head = k; xl[0].tail = xl[1].tail = k - 1;
xl[0].push_back(p{k, dp[k] + 2*(n-k)*s[k]});
xl[1].push_front(p{k, dp[k] - 2*(k-1)*s[k]});
while(tot--){
if(l == 1 || r == n) break;
p ans = xl[0].get_rtol(-2*s[l-1]);
dp[l-1] = ans.second + 2*ans.first*s[l-1] - 2*n*s[l-1];
ans = xl[1].get_ltor(-2*s[r+1]);
dp[r+1] = ans.second + 2*ans.first*s[r+1] - 2*s[r+1];
if(dp[l-1] <= dp[r+1]) --l, xl[1].push_front({l, dp[l] - 2*(l-1)*s[l]});
else ++r, xl[0].push_back({r, dp[r] + 2*(n-r)*s[r]});
} wt(min(dp[1], dp[n])); return fw, 0;
}

本文作者:XiaoLe_MC

本文链接:https://www.cnblogs.com/xiaolemc/p/18425910

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   XiaoLe_MC  阅读(10)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起