九省联考总贴

秘密袭击

九省联考感觉有区分度,质量也挺高的。

单独写了。

详情见另一个博客。

#include <bits/stdc++.h>
using namespace std;

typedef unsigned int ui;

const int N = 2e3 + 5;
const int M = N * 2;
const ui mod = 64123;
const int MAX = 1e5 + 5;

struct NODE {
  ui a, b, c, d;
  friend NODE operator * (NODE er, NODE la) {
    return
      (NODE) {er.a * la.a % mod, (la.a * er.b % mod + la.b) % mod
      , (la.c * er.a % mod + er.c) % mod,
	(er.b * la.c % mod + la.d + er.d) % mod};
  }
  void init() {
    a = 1; b = c = d = 0;
  }
}T[MAX];

int n, k, W, d[N], fir[N], ne[M], to[M], cnt, rt[N], sz, ch[MAX][2];

#define lc (ch[x][0])
#define rc (ch[x][1])

void add(int x, int y) {
  ne[++ cnt] = fir[x];
  fir[x] = cnt;
  to[cnt] = y;
}

void link(int x, int y) {
  add(x, y);
  add(y, x);
}

#define Foreachson(i, x) for(int i = fir[x]; i; i = ne[i])

void readin() {
  int x, y;
  scanf("%d%d%d", &n, &k, &W);
  for(int i = 1; i <= n; ++ i) scanf("%d", &d[i]);
  for(int i = 1; i < n; ++ i) {
    scanf("%d%d", &x, &y);
    link(x, y);
  }
}

ui poly[N], F[MAX], S[MAX];

int newnode() {
  ++ sz;
  F[sz] = S[sz] = 0;
  T[sz].init();
  ch[sz][0] = ch[sz][1] = 0;
  return sz;
}

void pt(int &x, NODE who) {
  if(!x) x = newnode();
  T[x] = T[x] * who;
  return;
}

void pd(int x) {
  pt(lc, T[x]);
  pt(rc, T[x]);
  T[x].init();
}

void chg(int &x, int l, int r, int L, int R, NODE who) {
  if(!x) x = newnode();
  if(l == L && r == R) {
    T[x] = T[x] * who;
    return;
  }
  pd(x);
  int mid = (l + r) >> 1;
  if(L > mid) chg(rc, mid + 1, r, L, R, who);
  else if(R <= mid) chg(lc, l, mid, L, R, who);
  else chg(lc, l, mid, L, mid, who), chg(rc, mid + 1, r, mid + 1, R, who);
}

int merge(int x, int y) {
  if(!x || !y) return x + y;
  if(!ch[x][0] && !ch[x][1]) swap(x, y);
  if(!ch[y][0] && !ch[y][1]) {
    // y's is ((a + b), d)
    T[x].a = T[x].a * T[y].b % mod;
    T[x].b = T[x].b * T[y].b % mod;
    T[x].d = (T[x].d + T[y].d) % mod;
    return x;
  }
  pd(x); pd(y);
  ch[x][0] = merge(ch[x][0], ch[y][0]);
  ch[x][1] = merge(ch[x][1], ch[y][1]);
  return x;
}

void dfs(int x, int f, ui magic) {
  rt[x] = newnode();
  pt(rt[x], (NODE){0, 1, 0, 0});
  Foreachson(i, x) {
    int V = to[i];
    if(V == f) continue;
    dfs(V, x, magic);
    rt[x] = merge(rt[x], rt[V]);
  }
  if(d[x]) chg(rt[x], 1, W, 1, d[x], (NODE){magic, 0, 0, 0});
  pt(rt[x], (NODE){1, 0, 1, 0});
  pt(rt[x], (NODE){1, 1, 0, 0});
}

void query(int &x, int l, int r, ui &ans) {
  if(l == r) {
    ans = ans + (T[x].d);
    ans %= mod;
    return;
  }
  int mid = (l + r) >> 1;
  pd(x);
  query(lc, l, mid, ans), query(rc, mid + 1, r, ans);
}

ui f[N], g[N];

int Pow(ui x, int y) {
  ui res = 1;
  for(; y; y >>= 1, x = 1LL * x * x % mod) {
    if(y & 1) {
      res = 1LL * res * x % mod;
    }
  }
  return res;
}

void dec(ui *f, ui *g, int x) {
  for(int i = 0; i <= n + 1; ++ i) g[i] = f[i];
  ui Inv = Pow(x, mod - 2);
  for(int i = 0; i <= n; ++ i) {
    g[i] = 1LL * g[i] * (mod - Inv) % mod;
    g[i + 1] = (g[i + 1] - g[i] + mod) % mod;
  }
  assert(!g[n + 1]);
}

void Lagrange(void) {
  memset(f, 0, sizeof(f));
  memset(g, 0, sizeof(g));
  f[0] = 1;
  for(int i = 1; i <= n + 1; ++ i) 
    for(int j = n + 1; j >= 0; -- j)
      f[j + 1] = (f[j + 1] + f[j]) % mod,
	f[j] = 1LL * f[j] * (mod - i) % mod;
  ui ans = 0;
  for(int i = 1; i <= n + 1; ++ i) {
    dec(f, g, i);
    ui now = 0;
    for(int j = k; j <= n; ++ j) now = (now + g[j]) % mod;
    for(int j = 1; j <= n + 1; ++ j) {
      if(i != j)
	now = now * Pow((i - j + mod) % mod, mod - 2) % mod;
    }
    now = now * poly[i] % mod;
    ans = (ans + now) % mod;
  }
  cout << ans << endl;
}

int main() {
  readin();
  for(int i = 1; i <= n + 1; ++ i) {
    sz = 0;
    dfs(1, 0, i);
    query(rt[1], 1, W, poly[i]);
    //poly[i] = (poly[i] + query(rt[1], 1, W, j)) % mo;
    cerr << i <<" " << poly[i] << endl;
  }
  Lagrange();
}

劈配

劈配不难,这只需要在每一次增广以后看一下哪一些点能够增广到终点就可以了。
我的小错误: 清空的时候不要上次用多少清空多少,尽量清空出这次需要用的量,否则容易在边界上出问题。 比如我这里就少处理了一位导致WA一个点。

#include <bits/stdc++.h>
using namespace std;

const int N = 1e3 + 5;
const int M = N * 100;

vector <int> T[N][N];

int n, m, b[N], x, li[N], fir[N], ne[M], to[M], cnt = 1, cur[N], ttt, c, C[M];

void add(int x, int y, int z) {
  ne[++ cnt] = fir[x];
  fir[x] = cnt;
  to[cnt] = y;
  C[cnt] = z;
}

void link(int x, int y, int z) {
  add(x, y, z);
  add(y, x, 0);
}

#define Foreachson(i, x) for(int i = fir[x]; i; i = ne[i])
#define Forcurbegin(i, x) for(int &i = cur[x]; i; i = ne[i])
#define REP(i, a, b) for(int i = (a); i <= (b); ++ i)
#define PER(i, a, b) for(int i = (a); i >= (b); -- i)
#define SZ(x) (int)x.size()
#define ALL(x) x.begin(), x.end()
#define CLR(x) memset(x, 0, sizeof(x))

int dis[N], s, t;
bitset <N> can[N];

bool BFS(int s, int t) {
  for(int i = 1; i <= n + m + 2; ++ i) dis[i] = -1, cur[i] = fir[i];
  queue <int> q; while(!q.empty()) q.pop();
  q.push(s); dis[s] = 0;
  while(!q.empty()) {
    int ind = q.front();
    q.pop();
    Foreachson(i, ind) if(C[i]) {
      int V = to[i];
      if(dis[V] != -1) continue;
      dis[V] = dis[ind] + 1;
      q.push(V);
      if(V == t) return 1;
    }
  }
  return 0;
}

int dfs(int x, int fl, int t) {
  if(!fl || x == t) return fl;
  int res = 0;
  Forcurbegin(i, x) if(dis[to[i]] == dis[x] + 1 && C[i]) {
    int V = to[i];
    int ret = dfs(V, min(fl, C[i]), t);
    if(!ret) continue;
    res += ret;
    fl -= ret;
    C[i] -= ret;
    C[i ^ 1] += ret;
    if(!fl) break;
  }
  return res;
}

int Left(int x) {
  return x + 1;
}

int Right(int x) {
  return x + n + 1;
}

int ans1[N], ans2[N];

void init() {
  cin >> n >> m;
  CLR(b); CLR(li); CLR(fir); CLR(ne); CLR(to); CLR(C); CLR(cur);
  cnt = 1;
  REP(i, 1, n) {
    can[i] = 0;
    ans1[i] = ans2[i] = 0;
    REP(j, 0, m) T[i][j].clear();
  }
  s = 1, t = n + m + 2;
  REP(i, 1, m) scanf("%d", &b[i]), link(Right(i), t, b[i]);
  REP(i, 1, n) {
    REP(j, 1, m) scanf("%d", &x), T[i][x].push_back(j);
  }
  REP(i, 1, n) scanf("%d", &li[i]);
}

void solve() {
  can[1] = 0;
  REP(i, 1, m) if(b[i]) can[1][i] = 1;
  REP(i, 1, n) {
    int who = m + 1;
    REP(j, 1, m) {
      bool ok = 0;
      REP(k, 0, SZ(T[i][j]) - 1) {
	if(can[i][T[i][j][k]]) {
	  ok = 1; break;
	}
      }
      if(ok) {
	who = j;
	link(s, Left(i), 1);
	REP(k, 0, SZ(T[i][j]) - 1)
	  link(Left(i), Right(T[i][j][k]), 1);
	BFS(s, t);
	dfs(s, 1e9, t);
	break;
      }
    }
    bitset < N > need; need = 0;
    ans1[i] = who;
    REP(j, 1, li[i]) REP(k, 0, SZ(T[i][j]) - 1) need[T[i][j][k]] = 1;
    ans2[i] = i;
    PER(j, i, 1) if((need & can[j]).count()) {
      ans2[i] = i - j; break;
    }
    REP(j, 1, m) if(BFS(Right(j), t)) can[i + 1][j] = 1;
  }
  for(int i = 1; i <= n; ++ i) printf("%d ", ans1[i]); puts("");
  for(int i = 1; i <= n; ++ i) printf("%d ", ans2[i]); puts("");
}

int main() {
  cin >> ttt >> c;
  for(; ttt --;) {
    init();
    solve();
  }
}

IIIDX

这个题我觉得出题人是对着idea造的题。
首先我们先解决一个子问题: 每次给定一个操作 ( a , b ) (a,b) (a,b),表示这次操作 [ 1 , a ] [1,a] [1,a]这个区间里选择了b个东西删去,询问最前面可能出现的 j j j 使得 ∑ i = 1 j A i \sum _{i=1}^{j} A_i i=1jAi最大 A i A_i Ai表示第i个东西是否没被选择。 这个问题首先我的一个思路是区间等差数列加求最小值,这样就是一个李超树的问题。然而这个题有一个比较优美的性质,我们只需要给 [ a , n ] [a,n] [a,n]的所有数减去b,然后查询的时候查询一个后缀是否可行就行了。
这个题我首先想到了一个只能过60pts对于互不相同能够出解的贪心,后来看到这一档部分分感觉不太对,然后手膜了一下发现这个只能过60pts。然后就不会捉了。
后来发现我不会做的原因是没有利用到这棵树儿子编号是连续的的性质,下次对于这种一个树的儿子都是连在一起做的问题,大胆的push一个子树的贡献,然后在考虑儿子时撤销。
其实正解就是直接从左到右考虑按位确定,每一次需要预定一个子树里的所有值,这对应了我上面说的先把父亲的一个子树的贡献计算了,然后到儿子的时候撤销。



#include <bits/stdc++.h>
using namespace std;

const int N = 5e5 + 5;
const int Segment_Size = N * 4;

int L[Segment_Size], R[Segment_Size];
long long Min[Segment_Size], tag[Segment_Size];
int n, d[N], fa[N], pos[N], Size[N];
double k;

#define lc (no << 1)
#define rc (no << 1 | 1)
#define getmid int mid = (L[no] + R[no]) >> 1

void up(int no) {
  Min[no] = min(Min[lc], Min[rc]);
}

void build(int no, int l, int r) {
  L[no] = l, R[no] = r;
  if(l == r) {
    tag[no] = 0;
    Min[no] = L[no];
    return;
  }
  getmid;
  build(lc, l, mid);
  build(rc, mid + 1, r);
  up(no);
}

void pt(int no, int x) {
  tag[no] += x;
  Min[no] += x;
}

void pd(int no) {
  if(L[no] == R[no] || !tag[no]) return;
  pt(lc, tag[no]);
  pt(rc, tag[no]);
  tag[no] = 0;
  up(no);
}

int query(int no, int x) {
  if(L[no] == R[no]) {
    if(Min[no] >= x) return L[no];
    else return L[no] + 1;
  }
  pd(no);
  if(Min[rc] >= x) return query(lc, x);
  else return query(rc, x);
}

void chg(int no, int l, int r, int x) {
  if(L[no] == l && R[no] == r) {
    pt(no, x);
    return;
  }
  pd(no);
  getmid;
  if(l > mid) chg(rc, l, r, x);
  else if(r <= mid) chg(lc, l, r, x);
  else chg(lc, l, mid, x), chg(rc, mid + 1, r, x);
  up(no);
}

map <int, int> Map;

int main() {
  cin >> n >> k;
  for(int i = 1; i <= n; ++ i) scanf("%d", &d[i]);
  sort(d + 1, d + n + 1);
  reverse(d + 1, d + n + 1);
  for(int i = 1; i <= n; ++ i) {
    Map[d[i]] = i;
  }
  for(int i = 1; i <= n; ++ i) {
    fa[i] = ((int)(i / k));
  }
  for(int i = n; i >= 1; -- i) Size[i] += 1, Size[fa[i]] += Size[i];
  build(1, 1, n);
  for(int i = 1; i <= n; ++ i) {
    if(fa[i] && fa[i] != fa[i - 1]) chg(1, pos[fa[i]], n, Size[fa[i]] - 1);
    int now = query(1, Size[i]);
    pos[i] = Map[d[now]];
    //cerr << i <<" " << now << endl;
    chg(1, pos[i], n, -Size[i]);
  }
  for(int i = 1; i <= n; ++ i) printf("%d ", d[pos[i]]);
  puts("");
}

林克卡特树

首先一个比较 t r i v i a l trivial trivial的结论就是我们选择k条不相交的链的最大长度和就是答案。然后发现显然可以费用流,而这个费用流又是凸的,所以就可以WQS二分了。
较为套路。
值得注意的是这类题目二分的边界需要特别考虑!

#include <bits/stdc++.h>
using namespace std;

const int N = 3e5 + 5;
const int M = N * 2;

int n, k, x, y, z, fir[N], ne[M], to[M], cnt, C[M];

void add(int x, int y, int z) {
  ne[++ cnt] = fir[x];
  fir[x] = cnt;
  to[cnt] = y;
  C[cnt] = z;
}

void link(int x, int y, int z) {
  add(x, y, z);
  add(y, x, z);
}

#define Foreachson(i, x) for(int i = fir[x]; i; i = ne[i])

struct NODE {
  long long Val, num;
  friend bool operator < (NODE a, NODE b) {
    return (a.Val == b.Val) ? a.num < b.num : a.Val < b.Val;
  }
  friend NODE operator + (NODE a, NODE b) {
    return (NODE){a.Val + b.Val, a.num + b.num};
  }
};

long long ret, magic;
NODE Dp[N][3];

NODE upd(NODE x) {
  return (NODE){x.Val - magic, x.num + 1};
}

int tot = 0;

void dfs(int x, int f) {
  NODE tmp[4];
  tmp[1] = tmp[2] = tmp[0] = (NODE){0, 0};
  Foreachson(i, x) {
    int V = to[i];
    if(V == f) continue;
    dfs(V, x);
    Dp[V][1].Val += C[i];
    for(int a = 2; a >= 0; -- a)
      tmp[a + 1] = max(tmp[a] + Dp[V][1], tmp[a + 1]),
	tmp[a] = max(tmp[a], tmp[a] + Dp[V][0]);
  }
  Dp[x][0] = max(upd(tmp[1]), max(max(tmp[0], upd(tmp[0])), upd(tmp[2])));
  //cerr << x <<" " << f << " " << tmp[0].Val <<" " << tmp[0].num <<" " << Dp[x][0].Val <<" " << Dp[x][0].num << endl;
  Dp[x][1] = max(tmp[1], tmp[0]);
}

int solve(int T) {
  magic = T;
  dfs(1, 0);
  return Dp[1][0].num;
}

int main() {
  cin >> n >> k;
  ++ k;
  for(int i = 1; i < n; ++ i) {
    scanf("%d%d%d", &x, &y, &z);
    link(x, y, z);
  }
  long long L = -1e12, R = 1e12, res = 0;
  while(L <= R) {
    int mid = (L + R) >> 1;
    if(solve(mid) < k) R = mid - 1;
    else L = mid + 1, res = mid;
  }
  //if(solve(res + 1) == k) ++ res;
  solve(res);
  //cerr << Dp[1][0].Val << endl;
  printf("%lld\n", Dp[1][0].Val + 1LL * k * res);
}

一双木棋

一眼题不说了。



#include <bits/stdc++.h>
using namespace std;

const int N = 15;

map < long long, int> Dp;

int n, m, x, y, A[N][N], B[N][N];
long long Pm[N];
long long MAX = 0;

int solve(long long x) {
  if(Dp.count(x)) return Dp[x];
  Dp[x] = -2e9;
  int tmp[N] = {0};
  long long now = x;
  for(int i = 1; i <= n; ++ i) {
    tmp[i] = now % (m + 1);
    now /= (m + 1);
  }
  tmp[0] = 1e9;
  int res = 0;
  for(int i = 1; i <= n; ++ i) res += (tmp[i]);
  for(int i = 1; i <= n; ++ i) {
    if(tmp[i - 1] > tmp[i] && tmp[i] < m) {
      long long y = x;
      y -= 1LL * Pm[i] * tmp[i];
      y += 1LL * Pm[i] * (tmp[i] + 1);
      //cerr << x <<" " << y <<" " << -solve(y) << " " << ((res & 1) ? B[i][tmp[i] + 1] : A[i][tmp[i] + 1]) << " " << Dp[x] << " " << "in" << endl;
      Dp[x] =
    max(Dp[x], -solve(y) + ((res & 1) ? B[i][tmp[i] + 1] : A[i][tmp[i] + 1]));
      //cerr << x <<" " << y <<" " << -solve(y) << " " << ((res & 1) ? B[i][tmp[i] + 1] : A[i][tmp[i] + 1]) << " " << Dp[x] << endl;
    }
  }
  //for(int i = 1; i <= n; ++ i) printf("%d ", tmp[i]);
  // puts("");
  //cerr << Dp[x] << " -- " << res << endl;
  //cerr << endl;
  return Dp[x];
}

int main() {
  scanf("%d%d", &n, &m);
  for(int i = 1; i <= n; ++ i)
    for(int j = 1; j <= m; ++ j) scanf("%d", &A[i][j]);
  for(int i = 1; i <= n; ++ i)
    for(int j = 1; j <= m; ++ j) scanf("%d", &B[i][j]);
  long long now = 0;
  Pm[1] = 1;
  for(int i = 2; i <= n; ++ i) Pm[i] = 1LL * Pm[i - 1] * (m + 1);
  for(int i = 1; i <= n; ++ i) now += 1LL * Pm[i] * m;
  MAX = now;
  Dp[MAX] = 0;
  printf("%d\n", solve(0));
}

制胡窜

咕咕咕。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值