T1:给定两个长度为  的序列 
 你需要选择一个区间
,使得 
 且 
 。最大化你选择的区间长度
 解:枚举右端点,查最小的左端点
 需要 
 就是一个三维偏序,树套树
 发现枚举的  这一维没有用,因为如果查到了一个 
 的 
 是不会影响的
 于是按  排序,
 树状数组即可
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:给出一个二叉树的中序遍历的权值,最小化 ,
 考虑区间 ,
 表示把 
 建成一颗二叉树的最小代价
 枚举根 ,发现根的新增贡献为 
,两边的每个点的新增贡献为它们的权值,因为 
 增加 1
 于是有 
 看数据范围盲猜决策单调性,其实不用盲猜,你  挪到 
 比较显然 根也会往右移
 比较严谨的证明是首先  满足四边形不等式,所以可以证明 
 满足四边形不等式,由于 
 满足四边形不等式,具体可以参考 lyd 的书
 所以  的决策点在 
 的中间,复杂度 
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:
 给定一张  个点 
 条边的强连通有向图。
 初始时你在  号点,你会不停地从当前点的所有出边中等概率随机一条走过
 去。当你到达 k 号点的时候,你会栽跟头并且停止走路
 对于  ,分别求出你期望要走多少条边
题挺好的,部分分很足,先来讲一讲
 对于  的数据, 
 ,暴力枚举终点高斯消元
对于另外  的数据,不存在 
 使得 
 且 
 这个点是一个菊花图,也就是说在高斯消元的矩阵上,第一行每一列都有值
 第 列只有 1 和 
 有值,所以把 
 的每一行的 
 去把第一行的消掉,复杂度 
 加上枚举是  的
对于另外  的数据,前 
 条边满足 
 发现存在  行,假设它是第 
 行,那么第 
 列之前是没有值的
 也就是说给出来的矩阵是一个比较完美的上三角矩阵, 消元即可
正解:发现对于一个终点 ,做法是把 
 一行忽略掉,也就是它不参与消元
 所以对于 ,
 都是参与了消元了的,分治即可
 也可以这么理解,对于一个一行 ,它能去参与消元当且仅当 
,也就是说 
 可以参与 
 的消元,线段树分治即可,相对更好写
这里需要把高斯消元的板子变一下形,因为并不是从第一行开始消的:
 对于第一个考虑的行 ,把它挪到第一行,消去其它行的第 
 列
 这样是  的
 也就是说我们按消除行的顺序输出消完的矩阵是一个上三角矩阵
 不需要重排,记录一下第一行的实际值是什么即可
 递归到底层的时候需要对上三角矩阵求解,是  的,每一个算一遍是 
 而一个值会在线段树上有  个区间,所以它会去消 
 次
 然后有一个头疼的逆元,需要快速幂,复杂度 
 主要考察的是一类分治问题的套路与灵活运用高斯消元
 等等,方程还没有列 
 由于递归完了要还原,每一次开一个  数组即可
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];
}
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;
}                
                










