0
点赞
收藏
分享

微信扫一扫

CSP-S 模拟19/10/30

gy2006_sw 2022-07-12 阅读 113

T1:给定两个长度为 CSP-S 模拟19/10/30_数据 的序列 CSP-S 模拟19/10/30_数据_02 你需要选择一个区间
CSP-S 模拟19/10/30_i++_03,使得 CSP-S 模拟19/10/30_git_04CSP-S 模拟19/10/30_git_05 。最大化你选择的区间长度
解:枚举右端点,查最小的左端点
需要 CSP-S 模拟19/10/30_i++_06
就是一个三维偏序,树套树
发现枚举的 CSP-S 模拟19/10/30_git_07 这一维没有用,因为如果查到了一个 CSP-S 模拟19/10/30_git_08CSP-S 模拟19/10/30_i++_09 是不会影响的
于是按 CSP-S 模拟19/10/30_git_10 排序,CSP-S 模拟19/10/30_数据_11 树状数组即可

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 1e6 + 5;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
typedef long long ll;
int n;
struct node{ ll a, b; int id; } x[N];
bool cmp(node A, node B){ return A.a < B.a; }
ll c[N]; int siz;
cs int inf = 1e9;
struct BIT{
int c[N];
BIT(){ memset(c, 0x3f, sizeof(c)); }
void add(int x, int v){ for(;x<=siz;x+=x&-x) c[x] = min(c[x], v); }
int ask(int x){ int ans = inf; for(;x;x-=x&-x) ans = min(ans, c[x]); return ans; }
}bit;
int main(){
n = read(); x[0].a = x[0].b = 0; x[0].id = 0; c[++siz] = 0;
for(int i = 1; i <= n; i++) x[i].a = x[i-1].a + (ll)read(), x[i].id = i;
for(int i = 1; i <= n; i++) x[i].b = x[i-1].b + (ll)read(), c[++siz] = x[i].b;
sort(x, x + n + 1, cmp);
sort(c + 1, c + siz + 1); siz = unique(c + 1, c + siz + 1) - (c + 1);
int ans = 0;
for(int i = 0; i <= n; i++){
int p = lower_bound(c + 1, c + siz + 1, x[i].b) - c;
ans = max(ans, x[i].id - bit.ask(p));
bit.add(p, x[i].id);
} cout << ans; return 0;
}

T2:给出一个二叉树的中序遍历的权值,最小化 CSP-S 模拟19/10/30_数据_12CSP-S 模拟19/10/30_git_13
考虑区间 CSP-S 模拟19/10/30_数据_14CSP-S 模拟19/10/30_git_15 表示把 CSP-S 模拟19/10/30_i++_16 建成一颗二叉树的最小代价
枚举根 CSP-S 模拟19/10/30_数据_17,发现根的新增贡献为 CSP-S 模拟19/10/30_i++_18,两边的每个点的新增贡献为它们的权值,因为 CSP-S 模拟19/10/30_git_19 增加 1
于是有 CSP-S 模拟19/10/30_i++_20
看数据范围盲猜决策单调性,其实不用盲猜,你 CSP-S 模拟19/10/30_git_07 挪到 CSP-S 模拟19/10/30_git_22 比较显然 根也会往右移
比较严谨的证明是首先 CSP-S 模拟19/10/30_i++_23 满足四边形不等式,所以可以证明 CSP-S 模拟19/10/30_数据_24 满足四边形不等式,由于 CSP-S 模拟19/10/30_数据_24 满足四边形不等式,具体可以参考 lyd 的书
所以 CSP-S 模拟19/10/30_i++_16 的决策点在 CSP-S 模拟19/10/30_数据_27 的中间,复杂度 CSP-S 模拟19/10/30_数据_28

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 5e3 + 5;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
typedef long long ll;
int n; ll sum[N], f[N][N];
int p[N][N]; // 决策点
int main(){
n = read();
for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + (ll)read();
for(int l = n; l >= 1; l--){
for(int r = l; r <= n; r++){
f[l][r] = sum[r] - sum[l - 1];
if(l == r){ p[l][r] = l; continue; }
ll ret = 1e15;
for(int k = p[l][r-1]; k <= p[l+1][r]; k++){
if(f[l][k - 1] + f[k + 1][r] < ret){
p[l][r] = k; ret = f[l][k - 1] + f[k + 1][r];
}
} f[l][r] += ret;
}
} cout << f[1][n]; return 0;
}

T3:
给定一张 CSP-S 模拟19/10/30_数据 个点 CSP-S 模拟19/10/30_i++_30 条边的强连通有向图。
初始时你在 CSP-S 模拟19/10/30_git_31 号点,你会不停地从当前点的所有出边中等概率随机一条走过
去。当你到达 k 号点的时候,你会栽跟头并且停止走路
对于 CSP-S 模拟19/10/30_git_32 ,分别求出你期望要走多少条边
CSP-S 模拟19/10/30_i++_33

题挺好的,部分分很足,先来讲一讲
对于 CSP-S 模拟19/10/30_i++_34 的数据, CSP-S 模拟19/10/30_i++_35 ,暴力枚举终点高斯消元

对于另外 CSP-S 模拟19/10/30_git_36 的数据,不存在 CSP-S 模拟19/10/30_数据_37 使得 CSP-S 模拟19/10/30_i++_38CSP-S 模拟19/10/30_数据_39
这个点是一个菊花图,也就是说在高斯消元的矩阵上,第一行每一列都有值
CSP-S 模拟19/10/30_数据_40 列只有 1 和 CSP-S 模拟19/10/30_数据_40 有值,所以把 CSP-S 模拟19/10/30_数据_42 的每一行的 CSP-S 模拟19/10/30_数据_40 去把第一行的消掉,复杂度 CSP-S 模拟19/10/30_i++_44
加上枚举是 CSP-S 模拟19/10/30_数据_28

对于另外 CSP-S 模拟19/10/30_数据_46 的数据,前 CSP-S 模拟19/10/30_数据_47 条边满足 CSP-S 模拟19/10/30_git_48
发现存在 CSP-S 模拟19/10/30_i++_49 行,假设它是第 CSP-S 模拟19/10/30_数据_40 行,那么第 CSP-S 模拟19/10/30_数据_40 列之前是没有值的
也就是说给出来的矩阵是一个比较完美的上三角矩阵,CSP-S 模拟19/10/30_数据_28 消元即可

正解:发现对于一个终点 CSP-S 模拟19/10/30_数据_53,做法是把 CSP-S 模拟19/10/30_数据_53 一行忽略掉,也就是它不参与消元
所以对于 CSP-S 模拟19/10/30_git_55CSP-S 模拟19/10/30_数据_56 都是参与了消元了的,分治即可
也可以这么理解,对于一个一行 CSP-S 模拟19/10/30_数据_40,它能去参与消元当且仅当 CSP-S 模拟19/10/30_git_58,也就是说 CSP-S 模拟19/10/30_数据_40 可以参与 CSP-S 模拟19/10/30_数据_60 的消元,线段树分治即可,相对更好写

这里需要把高斯消元的板子变一下形,因为并不是从第一行开始消的:
对于第一个考虑的行 CSP-S 模拟19/10/30_数据_40,把它挪到第一行,消去其它行的第 CSP-S 模拟19/10/30_数据_40
这样是 CSP-S 模拟19/10/30_数据_28
也就是说我们按消除行的顺序输出消完的矩阵是一个上三角矩阵
不需要重排,记录一下第一行的实际值是什么即可
递归到底层的时候需要对上三角矩阵求解,是 CSP-S 模拟19/10/30_数据_28 的,每一个算一遍是 CSP-S 模拟19/10/30_数据_65
而一个值会在线段树上有 CSP-S 模拟19/10/30_i++_66 个区间,所以它会去消 CSP-S 模拟19/10/30_i++_66
然后有一个头疼的逆元,需要快速幂,复杂度 CSP-S 模拟19/10/30_数据_68
主要考察的是一类分治问题的套路与灵活运用高斯消元
等等,方程还没有列 CSP-S 模拟19/10/30_i++_69
由于递归完了要还原,每一次开一个 CSP-S 模拟19/10/30_i++_70 数组即可

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 305, M = 1e5 + 5;
cs int Mod = 998244353;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt * 10 + (ch-'0'), ch = getchar();
return cnt * f;
}
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b;}
int mul(int a, int b){ return 1ll * a * b % Mod; }
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans = mul(ans, a); return ans; }
int n, m, du[N];
int first[N], nxt[M], to[M], tot;
void adde(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y; }
int a[N][N], tp[20][N][N], ans[N];
bool vis[N];
int top, idx[N]; bool Svis[20][N];
void Gauss(int i){ // 用第 i 行去消其它的
vis[i] = true;
idx[++top] = i;
for(int j = 1; j <= n; j++){
if(vis[j]) continue;
int delta = mul(a[j][i], ksm(a[i][i], Mod-2));
if(!delta) continue;
for(int k = 1; k <= n+1; k++){
a[j][k] = add(a[j][k], Mod - mul(delta, a[i][k]));
}
}
}
int b[N][N];
int calc(){
for(int i = n; i >= 1; i--){
if(!vis[idx[i]]) continue;
for(int j = i + 1; j <= n; j++) {
if(!vis[idx[j]]) continue;
a[idx[i]][n + 1] = add(a[idx[i]][n + 1], Mod - mul(a[idx[i]][idx[j]], a[idx[j]][n + 1]));
} a[idx[i]][n + 1] = mul(a[idx[i]][n + 1], ksm(a[idx[i]][idx[i]], Mod - 2));
}
return a[idx[1]][n + 1];
}
#define mid ((l+r)>>1)
vector<int> v[N << 2];
void Push(int x, int l, int r, int L, int R, int p){
if(L>R) return;
if(L<=l && r<=R){ v[x].push_back(p); return; }
if(L<=mid) Push(x<<1, l, mid, L, R, p);
if(R>mid) Push(x<<1|1, mid+1, r, L, R, p);
}
void Solve(int dep, int x, int l, int r){
memcpy(tp[dep], a, sizeof(a));
memcpy(Svis[dep], vis, sizeof(vis));
for(int i = 0; i < v[x].size(); i++){
int p = v[x][i]; Gauss(p);
}
if(l == r){ if(l^1) ans[l] = calc(); }
else{
Solve(dep + 1, x << 1, l, mid);
Solve(dep + 1, x << 1|1, mid+1, r);
}
memcpy(a, tp[dep], sizeof(tp[dep]));
memcpy(vis, Svis[dep], sizeof(Svis[dep]));
for(int i = 0; i < v[x].size(); i++) idx[top--] = 0;
}

int main(){
n = read(), m = read();
for(int i = 1; i <= m; i++){
int x = read(), y = read();
adde(x, y); ++du[x];
}
for(int i = 1; i <= n; i++){
a[i][i] = 1;
int inv = ksm(du[i], Mod - 2);
for(int e = first[i]; e; e = nxt[e]){
int t = to[e]; a[i][t] = add(a[i][t], Mod - inv);
} a[i][n + 1] = 1;
}
Push(1, 1, n, 1, n, 1);
for(int i = 2; i <= n; i++) Push(1, 1, n, 1, i-1, i), Push(1, 1, n, i+1, n, i);
Solve(0, 1, 1, n);
for(int i = 2; i <= n; i++) cout << ans[i] << '\n';
return 0;
}


举报

相关推荐

0 条评论