ZJOI 2019 简要题解

从这里开始

  感觉就没几个题能写,不过暴力分确实给的很多。每日一吹 scoi 2019

Round 1

Problem A 麻将

  考虑怎么判断,先判断有没有超过 $7$ 种大小大于等于 2 ,然后依次考虑每种大小,设 $f_{i, j, 0/1}$ 表示前一种和前面第 2 种分别留下了多少个用于凑面子,以及有没有对子的情况下最多能凑出多少面子。注意到 $j \geqslant i$,所以只用记录 $j' = j - i$,又因连续三个留下了至少 3 个,可以变成 $(x, x, x), (x + 1, x + 1, x + 1), (x + 2, x + 2, x+2)$,因此有 $j', i \leqslant 2$。

  然后做一些简单标准化然后爆搜发现状态数并不多。注意 dp 值不能超过 4 。

  剩下就考虑选出 $i$ 张牌还没有胡的概率。相信这个大家都会。

Code

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

template <typename T>
bool vmax(T& a, T b) {
  return a < b ? (a = b, true) : false;
}

#define ll long long

void exgcd(int a, int b, int& x, int& y) {
  if (!b) {
    x = 1, y = 0;
  } else {
    exgcd(b, a % b, y, x);
    y -= (a / b) * x;
  }
}

int inv(int a, int n) {
  int x, y;
  exgcd(a, n, x, y);
  return (x < 0) ? (x + n) : (x);
}

const int Mod = 998244353;

template <const int Mod = :: Mod>
class Z {
  public:
    int v;

    Z() : v(0) {	}
    Z(int x) : v(x){	}
    Z(ll x) : v(x % Mod) {	}

    friend Z operator + (const Z& a, const Z& b) {
      int x;
      return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
    }
    friend Z operator - (const Z& a, const Z& b) {
      int x;
      return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
    }
    friend Z operator * (const Z& a, const Z& b) {
      return Z(a.v * 1ll * b.v);
    }
    friend Z operator ~(const Z& a) {
      return inv(a.v, Mod);
    }
    friend Z operator - (const Z& a) {
      return Z(0) - a;
    }
    Z& operator += (Z b) {
      return *this = *this + b;
    }
    Z& operator -= (Z b) {
      return *this = *this - b;
    }
    Z& operator *= (Z b) {
      return *this = *this * b;
    }
    friend boolean operator == (const Z& a, const Z& b) {
      return a.v == b.v;
    } 
};

Z<> qpow(Z<> a, int p) {
  Z<> rt = Z<>(1), pa = a;
  for ( ; p; p >>= 1, pa = pa * pa) {
    if (p & 1) {
      rt = rt * pa;
    }
  }
  return rt;
}

typedef Z<> Zi;

typedef class Status {
  public:
    int f[2][3][3], cnt;

    void reset() {
      for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
          for (int k = 0; k < 3; k++) {
            f[i][j][k] = -142857;
          }
        }
      }
      cnt = 0;
    }
    void standard() {
      for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
          for (int k = 0; k < 3; k++) {
            if (f[i][j][k] < 0) {
              f[i][j][k] = -142857;
            }
            f[i][j][k] = min(f[i][j][k], 4);
          }
        }
      }
    }

    bool operator < (Status b) const {
      for (int i = 0; i < 2; i++) {
        for (int j = 0; j <= 2; j++) {
          for (int k = 0; k <= 2; k++) {
            if (f[i][j][k] ^ b.f[i][j][k]) {
              return f[i][j][k] < b.f[i][j][k];
            }
          }
        }
      }
      return cnt < b.cnt;
    }
    bool valid() {
      if (cnt >= 7) {
        return false;
      }
      for (int j = 0; j <= 2; j++) {
        for (int k = 0; k <= 2; k++) {
          if (f[1][j][k] >= 4) {
            return false;
          }
        }
      }
      return true;
    }

    Status extend(int c) {
      Status nf;
      nf.reset();
      nf.cnt = cnt + (c >= 2);
      for (int j = 0; j <= 2; j++) {
        for (int k = 0; k <= 2; k++) {
          for (int t = 0; t <= j && t <= c; t++) {
            int nj = min(k + j - t, min(2, c - t));
            int nk = min(2, c - t - nj);
            vmax(nf.f[0][nj][nk], f[0][j][k] + t);
            vmax(nf.f[1][nj][nk], f[1][j][k] + t);
          }
          if (c >= 3) {
            for (int t = 0; t <= j && t <= c - 3; t++) {
              int nj = min(k + j - t, min(2, c - 3 - t));
              int nk = c - t - nj - 3;
              vmax(nf.f[0][nj][nk], f[0][j][k] + t + 1);
              vmax(nf.f[1][nj][nk], f[1][j][k] + t + 1);
            }  
          }
          if (c >= 2) {
            for (int t = 0; t <= j && t <= c - 2; t++) {
              int nj = min(k + j - t, min(2, c - t - 2));
              int nk = c - t - nj - 2;
              vmax(nf.f[1][nj][nk], f[0][j][k] + t);
            }
          }
        }
      }
      nf.standard();
      return nf;
    }

    void log() {
      cerr <<"--- ";
      cerr << cnt << "\n";
      for (int i = 0; i <= 2; i++) {
        for (int j = 0; j <= 2; j++) {
          cerr << f[1][i][j] << " ";
        }
        cerr << '\n';
      }
      cerr << '\n';
      cerr << "----\n";
    }
} Status;

const int S = 4000;
const Zi comb[5][5] = {{1}, {1, 1}, {1, 2, 1}, {1, 3, 3, 1}, {1, 4, 6, 4, 1}};

int cnts = 0;
map<Status, int> Gs;
int tr[S][5];

int dfs(Status s) {
  if (Gs.count(s)) {
    return Gs[s];
  }
  int x = cnts++;
  assert(cnts < S);
  Gs[s] = x;
  for (int c = 0; c <= 4; c++) {
    Status t = s.extend(c);
    if (t.valid()) {
      tr[x][c] = dfs(t);
    } else {
      tr[x][c] = -1;
    }
  }
  return x;
}

int n;
int cnt[105];
Zi f[2][405][S];

int main() {
  Status s0;
  s0.reset();
  s0.f[0][0][0] = 0;
  dfs(s0);
  scanf("%d", &n);
  for (int i = 1, x; i <= 13; i++) {
    scanf("%d%*d", &x);
    cnt[x]++;
  }
  f[0][0][0] = 1;
  int cur = 0;
  for (int i = 1; i <= n; i++) {
    memset(f[cur ^= 1], 0, sizeof(f[0]));
    for (int j = 0; j <= (i - 1) * 4; j++) {
      for (int s = 0; s < cnts; s++) {
        Zi v = f[cur ^ 1][j][s];
        if (!v.v) {
          continue;
        }
        int hc = cnt[i];
        for (int c = 0; c + hc < 5; c++) {
          int ns = tr[s][c + hc];
          if (~ns) {
            f[cur][j + c][ns] += v * comb[4 - hc][c];
          }
        }
      }
    }
  }
  int T = 4 * n - 13;
  vector<Zi> fac (T + 1);
  fac[0] = 1;
  for (int i = 1; i <= T; i++) {
    fac[i] = fac[i - 1] * i;
  }
  Zi ans = 0;
  for (int i = 0; i < T; i++) {
    Zi sum = 0;
    for (int s = 0; s < cnts; s++) {
      sum += f[cur][i][s];
    }
    ans += sum * fac[i] * fac[T - i];
  }
  ans *= ~fac[T];
  printf("%d\n", ans.v);
  return 0;
}

Problem B 线段树

  考虑一个点的贡献,问题相当于问有多少操作集合使得它的 $tg$ 为 1.考虑它到根的链上,显然只用记录三种状态:没有点有标记,它没有标记但其中某个点有,它有标记。然后修改相当于单点乘某个矩阵或者区间乘某个矩阵。

Code

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

#define ll long long

void exgcd(int a, int b, int& x, int& y) {
	if (!b) {
		x = 1, y = 0;
	} else {
		exgcd(b, a % b, y, x);
		y -= (a / b) * x;
	}
}

int inv(int a, int n) {
	int x, y;
	exgcd(a, n, x, y);
	return (x < 0) ? (x + n) : (x);
}

const int Mod = 998244353;

template <const int Mod = :: Mod>
class Z {
	public:
		int v;

		Z() : v(0) {	}
		Z(int x) : v(x){	}
		Z(ll x) : v(x % Mod) {	}

		friend Z operator + (const Z& a, const Z& b) {
			int x;
			return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
		}
		friend Z operator - (const Z& a, const Z& b) {
			int x;
			return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
		}
		friend Z operator * (const Z& a, const Z& b) {
			return Z(a.v * 1ll * b.v);
		}
		friend Z operator ~(const Z& a) {
			return inv(a.v, Mod);
		}
		friend Z operator - (const Z& a) {
			return Z(0) - a;
		}
		Z& operator += (Z b) {
			return *this = *this + b;
		}
		Z& operator -= (Z b) {
			return *this = *this - b;
		}
		Z& operator *= (Z b) {
			return *this = *this * b;
		}
		friend boolean operator == (const Z& a, const Z& b) {
			return a.v == b.v;
		} 
};

Z<> qpow(Z<> a, int p) {
	Z<> rt = Z<>(1), pa = a;
	for ( ; p; p >>= 1, pa = pa * pa) {
		if (p & 1) {
			rt = rt * pa;
		}
	}
	return rt;
}

typedef Z<> Zi;

const int N = 1e5 + 5;

typedef class Matrix {
	public:
		Zi a[3][3];

		Matrix() {
			memset(a, 0, sizeof(a));
		}

		Matrix operator + (Matrix b) {
			Matrix rt;
#define _(x, y) rt[x][y] = a[x][y] + b[x][y];
			_(0, 0) _(0, 1) _(0, 2) _(1, 0) _(1, 1) _(1, 2) _(2, 0) _(2, 1) _(2, 2)
#undef _
			return rt;
		}
		Matrix operator - (Matrix b) {
			Matrix rt;
#define _(x, y) rt[x][y] = a[x][y] - b[x][y];
			_(0, 0) _(0, 1) _(0, 2) _(1, 0) _(1, 1) _(1, 2) _(2, 0) _(2, 1) _(2, 2)
#undef _
			return rt;
		}
		Matrix operator * (Matrix b) {
			Matrix rt;
#define _(x, y) rt[x][y] = a[x][0] * b[0][y] + a[x][1] * b[1][y] + a[x][2] * b[2][y];
			_(0, 0) _(0, 1) _(0, 2) _(1, 0) _(1, 1) _(1, 2) _(2, 0) _(2, 1) _(2, 2)
#undef _
			return rt;
		}

		Zi* operator [] (int p) {
			return a[p];
		}
} Matrix;

typedef class SegTreeNode {
	public:
		bool has_tg;
		Matrix v, s, tg;
		SegTreeNode *l, *r;
		
		void upd(Matrix t) {
			v = v * t;
			s = s * t;
			if (has_tg) {
				tg = tg * t;
			} else {
				tg = t;
				has_tg = true;
			}
		}
		void upd(Matrix t, Matrix t1) {
			s = (s - v) * t1;
			v = v * t;
			s = s + v;
			if (has_tg) {
				tg = tg * t1;
			} else {
				tg = t1;
				has_tg = true;
			}
		}
		void _upd(Matrix x) {
			s = s - v;
			v = v * x;
			s = s + v;
		}
		void push_down() {
			if (has_tg) {
				l->upd(tg);
				r->upd(tg);
				has_tg = false;
			}
		}
		void push_up() {
			s = l->s + v + r->s;
		}
} SegTreeNode;

Matrix I, m1, m2, m3, m4, m5;

void prepare_matrix() {
	I[0][0] = I[1][1] = I[2][2] = 1;
	m1 = m2 = m3 = m4 = I;
	m1[0][0] += 1, m1[1][0] += 1, m1[2][0] += 1;
	m2[0][2] += 1, m2[1][2] += 1, m2[2][2] += 1;
	m3[0][1] += 1, m3[1][1] += 1, m3[2][2] += 1;
	m4[0][0] += 1, m4[1][2] += 1, m4[2][2] += 1;
	m5 = I + I;
}

typedef class SegmentTree {
	public:
		static SegTreeNode pool[N << 1];
		static SegTreeNode* top;

		static SegTreeNode* newnode() {
			top->v[0][0] = 1;
			top->has_tg = false;
			return top++;
		}
		
		int n, low;
		SegTreeNode* rt;

		SegmentTree() : rt(NULL) {	}
		
		void build(SegTreeNode*& p, int l, int r) {
			p = newnode();
			if (l ^ r) {
				int mid = (l + r) >> 1;
				build(p->l, l, mid);
				build(p->r, mid + 1, r);
				p->push_up();
			} else {
				p->s = p->v;
			}
		}

		void build(int n, int low = 1) {
			this->n = n;
			this->low = low;
			build(rt, low, n);
		}

		void modify(SegTreeNode* p, int l, int r, int ql, int qr) {
			if (l == ql && r == qr) {
				p->upd(m2, m3);
				return;
			}
			int mid = (l + r) >> 1;
			p->push_down();
			p->v = p->v * m1;
			if (qr <= mid) {
				modify(p->l, l, mid, ql, qr);
				p->r->upd(m4, m5);
			} else if (ql > mid) {
				modify(p->r, mid + 1, r, ql, qr);
				p->l->upd(m4, m5);
			} else {
				modify(p->l, l, mid, ql, mid);
				modify(p->r, mid + 1, r, mid + 1, qr);
			}
			p->push_up();
		}

		void modify(int l, int r) {
			assert(l >= low && r <= n);
			assert(l <= r);
			modify(rt, low, n, l, r);
		}
		Zi query() {
			return rt->s[0][2];
		}
} SegmentTree;

SegTreeNode SegmentTree :: pool[N << 1];
SegTreeNode* SegmentTree :: top = SegmentTree :: pool;

int n, m;
SegmentTree st;

int main() {
	prepare_matrix();
	scanf("%d%d", &n, &m);
	st.build(n);
	int op, x, y;
	while (m--) {
		scanf("%d", &op);
		if (op == 1) {
			scanf("%d%d", &x, &y);
			st.modify(x, y);
		} else {
			printf("%d\n", st.query().v);
		}
	}
	return 0;
}

Problem C Minimax 搜索

  假设最终根的权值 $x$ 。首先如果 $S$ 中包含 $x$,显然答案为 1,接下来不再考虑。

  否则考虑计算答案 $ > k$ 的方案数,不难发现只用考虑将根的权值变为 $x + 1$ 或者 $x - 1$。假设是变为 $x + 1$,那么将 $\leqslant  x$ 中能够修改的都改为 $x + 1$。然后将小于等于 $x$ 的记为 0,大于的记为 1,判断根节点的权值是否是 0。不难发现不能变为 $x + 1$ 和不能变为 $x - 1$ 的情况是独立,只用将它们的方案数乘起来。

  然后按某种顺序使得某些点可以修改,然后 ddp 维护一下就行了。

Code

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

#define ll long long

void exgcd(int a, int b, int& x, int& y) {
  if (!b) {
    x = 1, y = 0;
  } else {
    exgcd(b, a % b, y, x);
    y -= (a / b) * x;
  }
}

int inv(int a, int n) {
  int x, y;
  exgcd(a, n, x, y);
  return (x < 0) ? (x + n) : (x);
}

const int Mod = 998244353;

template <const int Mod = :: Mod>
class Z {
  public:
    int v;

    Z() : v(0) {	}
    Z(int x) : v(x){	}
    Z(ll x) : v(x % Mod) {	}

    friend Z operator + (const Z& a, const Z& b) {
      int x;
      return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
    }
    friend Z operator - (const Z& a, const Z& b) {
      int x;
      return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
    }
    friend Z operator * (const Z& a, const Z& b) {
      return Z(a.v * 1ll * b.v);
    }
    friend Z operator ~(const Z& a) {
      return inv(a.v, Mod);
    }
    friend Z operator - (const Z& a) {
      return Z(0) - a;
    }
    Z& operator += (Z b) {
      return *this = *this + b;
    }
    Z& operator -= (Z b) {
      return *this = *this - b;
    }
    Z& operator *= (Z b) {
      return *this = *this * b;
    }
    friend boolean operator == (const Z& a, const Z& b) {
      return a.v == b.v;
    } 
};

Z<> qpow(Z<> a, int p) {
  Z<> rt = Z<>(1), pa = a;
  for ( ; p; p >>= 1, pa = pa * pa) {
    if (p & 1) {
      rt = rt * pa;
    }
  }
  return rt;
}

typedef Z<> Zi;

const int N = 2e5 + 5;

typedef class Matrix {
  public:
    Zi a[2][2];

    void reset() {
      memset(a, 0, sizeof(a));
    }
    void setI() {
      a[0][0] = a[1][1] = 1;
    }
    Zi* operator [] (int p) {
      return a[p];
    }
    Matrix operator * (Matrix b) {
      Matrix rt;
#define G(x, y) rt[x][y] = a[x][0] * b[0][y] + a[x][1] * b[1][y];
      G(0, 0) G(0, 1) G(1, 0) G(1, 1);
#undef G
      return rt;
    }
} Matrix;

typedef class SegTreeNode {
  public:
    Matrix a;
    SegTreeNode *l, *r, *fa;
    
    void push_up() {
      a = l->a * r->a;
    }
} SegTreeNode;

SegTreeNode pool[N << 2];
SegTreeNode *_top;

SegTreeNode* newnode() {
  _top->a.reset();
  _top->l = _top->r = _top->fa = NULL;
  return _top++;
}

int n, _L, _R;
Zi ans[N];
int us[N], vs[N], dep[N];

vector<int> G[N];
int sz[N], zson[N], fa[N];

int dfs(int p, int fa) {
  ::fa[p] = fa;
  dep[p] = dep[fa] + 1;
  if (fa) {
    G[p].erase(find(G[p].begin(), G[p].end(), fa));
  }
  int rt = (G[p].empty()) ? (p) : ((dep[p] & 1) ? 0 : (n + 1));
  sz[p] = 1;
  for (auto e : G[p]) {
    int tmp = dfs(e, p);
    rt = (dep[p] & 1) ? max(rt, tmp) : min(rt, tmp);
    sz[p] += sz[e];
    if (sz[e] > sz[zson[p]]) {
      zson[p] = e;
    }
  }
  return rt;
}

SegTreeNode *rt1[N], *rt2[N];
int top[N], bot[N], stk[N], tp;

void build(SegTreeNode* &p, int l, int r, SegTreeNode** ar, const vector<int>& v) {
  if (l > r) {
    return;
  }
  p = newnode();
  p->a.setI();
  if (l == r) {
    ar[v[l]] = p;
    return;
  }
  int mid = (l + r) >> 1;
  build(p->l, l, mid, ar, v);
  build(p->r, mid + 1, r, ar, v);
  p->l->fa = p->r->fa = p;
}

void build() {
  for (int i = 1; i <= tp; i++) {
    top[stk[i]] = stk[tp];
    bot[stk[i]] = stk[1];
  }
  SegTreeNode* prt;
  build(prt, 0, tp - 1, rt1, vector<int>(stk + 1, stk + tp + 1));
  for (int i = 1; i <= tp; i++) {
    int p = stk[i];
    vector<int> ch = G[p];
    if (zson[p]) {
      ch.erase(find(ch.begin(), ch.end(), zson[p]));
    }
    build(prt, 0, (signed) ch.size() - 1, rt2, ch);
  }
  tp = 0;
}

void dfs(int p) {
  if (!G[p].empty()) {
    for (auto e : G[p]) {
      if (e ^ zson[p]) {
        dfs(e);
        build();
      }
    }
    dfs(zson[p]);
  }
  stk[++tp] = p;
}

Matrix pack(int p, Zi f0, Zi f1) { // p is the father
  Matrix rt;
  memset(rt.a, 0, sizeof(rt.a));
  if (dep[p] & 1) {
    rt[0][0] = f0, rt[0][1] = f1;
    rt[1][1] = f1 + f0;
  } else {
    rt[0][0] = f0 + f1;
    rt[1][0] = f0, rt[1][1] = f1;
  }
  return rt;
}

Zi modify(int x, Zi f0, Zi f1, int _ = 0) {
  Matrix a;
  memset(a.a, 0, sizeof(a.a));
  a[0][0] = f0, a[0][1] = f1;
  SegTreeNode* p;
  while (x) {
    p = rt1[x];
    p->a = a;
    for ( ; p->fa; (p = p->fa)->push_up());
    x = top[x];
    if (x == 1) {
      return p->a[0][_];
    }
    a = pack(fa[x], p->a[0][0], p->a[0][1]);
    p = rt2[x];
    p->a = a;
    for ( ; p->fa; (p = p->fa)->push_up());
    a = p->a;
    x = fa[x];
  }
  assert(false);
  return a[0][0];
}

int xrt, cntleaf = 0;
vector<Zi> work1() {
  vector<int> leaf;
  Zi rest = 1;
  for (int i = 1; i <= n; i++) {
    if (G[i].empty()) {
      modify(i, (i <= xrt), (i > xrt));
      if (i < xrt) {
        leaf.push_back(i);
        ++cntleaf;
        rest = rest + rest;
      }
    }
  }
  sort(leaf.begin(), leaf.end(), greater<int>());
  vector<Zi> f (n);
  auto it = leaf.begin(), _it = leaf.end();
  Zi cf = rest, inv2 = (Mod + 1) >> 1;
  for (int d = 0; d < n; d++) {
    while (it != _it && *it >= xrt - d + 1) {
      cf = modify(*it, 1, 1) * (rest *= inv2);
      it++;
    }
    f[d] = cf;
  }
  return f;
}

vector<Zi> work2() {
  vector<int> leaf;
  Zi rest = 1;
  for (int i = 1; i <= n; i++) {
    if (G[i].empty()) {
      modify(i, (i < xrt), (i >= xrt));
      if (i > xrt) {
        leaf.push_back(i);
        ++cntleaf;
        rest = rest + rest;
      }
    }
  }
  sort(leaf.begin(), leaf.end());
  vector<Zi> f (n);
  auto it = leaf.begin(), _it = leaf.end();
  Zi cf = rest, inv2 = (Mod + 1) >> 1;
  for (int d = 0; d < n; d++) {
    while (it != _it && *it <= xrt + d - 1) {
      cf = modify(*it, 1, 1, 1) * (rest *= inv2);
      it++;
    }
    f[d] = cf;
  }
  return f;
}

int main() {
  scanf("%d%d%d", &n, &_L, &_R);
  for (int i = 1, u, v; i < n; i++) {
    scanf("%d%d", &u, &v);
    G[u].push_back(v);
    G[v].push_back(u);
  }
  _top = pool;
  xrt = dfs(1, 0);
  dfs(1);
  build();
  auto a = work1();
  auto b = work2();
  for (int i = 0; i < n; i++) {
    ans[i + 1] = a[i] * b[i];
  }
  for (int i = 1; i <= n; i++) {
    ans[i] = ans[i] - ans[i + 1];
  }
  ans[1] += qpow(2, cntleaf);
  ans[n] -= 1;
  for (int i = _L; i <= _R; i++) {
    printf("%d%c", ans[i].v, (i == _R) ? ('\n') : ' ');
  } 
  return 0;
}

Round 2

Problem A 开关

  不考虑第一次的话,不难用 egf 表示出经过 $i$ 步从初始状态回到全 0 的生成函数是 $F(x)  = 2^{-n}\prod_i [e^{p_ix} + (-1)^{s_i}e^{-p_i x}] $。

  设从某个状态经过 $i$ 步回到它本身状态的 egf 是 $G(x) = 2^{-n}\prod_i [e^{p_ix} + (-1)^{s_i}e^{-p_i x}]$ 。

  设它们的 ogf 形式分别是 $f(x), g(x)$,设走 $i$ 步第一次回到全 0 的生成函数为 $h(x)$。

  显然有 $h(x) g(x) = f(x)$。注意到 $h'(1)$ 即为答案。考虑怎么求 $f, g$,注意到 $F(x) = \sum_t a_t e^{t x}$,这里可以直接把 $e^{t x}$ 换成 $\frac{1}{1 - tx}$ 来得到 ogf。

  现在的问题变成计算 $\left ( \frac{f(x)}{g(x)} \right )'\mid_{x = 1}$。你注意到分子分母都包含 $\frac{1}{1 - x}$,分子分母同乘 $(1 - x)$ 再套用求导的公式。 然后手算一下 $\left ( \frac{1 - x}{1 - kx} \right )'\mid_{x = 1} = \frac{1}{k - 1}$。

  剩下只用做一个暴力背包就行了。

Code

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

#define ll long long

void exgcd(int a, int b, int& x, int& y) {
  if (!b) {
    x = 1, y = 0;
  } else {
    exgcd(b, a % b, y, x);
    y -= (a / b) * x;
  }
}

int inv(int a, int n) {
  int x, y;
  exgcd(a, n, x, y);
  return (x < 0) ? (x + n) : (x);
}

const int Mod = 998244353;

template <const int Mod = :: Mod>
class Z {
  public:
    int v;

    Z() : v(0) {	}
    Z(int x) : v(x){	}
    Z(ll x) : v(x % Mod) {	}

    friend Z operator + (const Z& a, const Z& b) {
      int x;
      return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
    }
    friend Z operator - (const Z& a, const Z& b) {
      int x;
      return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
    }
    friend Z operator * (const Z& a, const Z& b) {
      return Z(a.v * 1ll * b.v);
    }
    friend Z operator ~(const Z& a) {
      return inv(a.v, Mod);
    }
    friend Z operator - (const Z& a) {
      return Z(0) - a;
    }
    Z& operator += (Z b) {
      return *this = *this + b;
    }
    Z& operator -= (Z b) {
      return *this = *this - b;
    }
    Z& operator *= (Z b) {
      return *this = *this * b;
    }
    friend boolean operator == (const Z& a, const Z& b) {
      return a.v == b.v;
    } 
};

Z<> qpow(Z<> a, int p) {
  Z<> rt = Z<>(1), pa = a;
  for ( ; p; p >>= 1, pa = pa * pa) {
    if (p & 1) {
      rt = rt * pa;
    }
  }
  return rt;
}

typedef Z<> Zi;

const int N = 105, V = 5e4 + 3;

int n, sp;
int s[N], p[N];
Zi f[V], g[V];

Zi calc(Zi* f) {
  Zi ret = 0;
  for (int i = 0; i < sp; i++) {
    ret += ~(Zi(i) - sp) * f[i];
  }
  return ret * sp * ((Mod + 1) >> 1);
}

int main() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) {
    scanf("%d", s + i);
  }
  for (int i = 1; i <= n; i++) {
    scanf("%d", p + i);
  }
  f[0] = g[0] = 1;
  for (int i = 1; i <= n; i++) {
    sp += p[i];
    for (int j = sp; j >= p[i]; j--) {
      g[j] += g[j - p[i]];
      f[j] = ((s[i]) ? (-f[j]) : f[j]) + f[j - p[i]];
    }
    if (s[i]) {
      for (int j = 0; j < p[i]; j++) {
        f[j] = -f[j];
      }
    }
  }
  Zi ans = calc(f) - calc(g);
  printf("%d\n", ans.v);
  return 0;
}

Problem B 语言

  启发式合并维护链并。

Code

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

typedef multiset<int>::iterator iter;

const int N = 1e5 + 5, N2 = N << 1;

template <typename T>
class SparseTable {
  public:
    int n;
    vector<T> Log2;
    vector<vector<T>> f;
    function<boolean(T, T)> compare;

    void init(int n, T* a, const function<boolean(T, T)>& compare = less<T>()) {
      this->n = n;
      this->compare = compare;
      Log2.resize(n + 1);
      Log2[0] = -1;
      for (int i = 1; i <= n; i++) {
        Log2[i] = Log2[i >> 1] + 1;
      }
      f.resize(Log2[n] + 1, vector<T>(n + 1));
      for (int i = 1; i <= n; i++) {
        f[0][i] = a[i];
      }
      for (int i = 1; i <= Log2[n]; i++) {
        for (int j = 1; j + (1 << i) - 1 <= n; j++) {
          T& x = f[i - 1][j], &y = f[i - 1][j + (1 << (i - 1))];
          if (compare(x, y)) {
            f[i][j] = x;
          } else {
            f[i][j] = y;
          }
        }
      }
    }

    T query(int l, int r) {
      int b = Log2[(r - l + 1)];
      T& x = f[b][l], &y = f[b][r - (1 << b) + 1];
      return compare(x, y) ? x : y;
    }
};

int n, m;
int dfc;
vector<int> G[N];
SparseTable<int> st;
int edep[N2], ein[N2], eout[N2], tour[N2];

int edist(int x, int y) { // euler order
  if (x > y) {
    swap(x, y);
  }
  return edep[x] + edep[y] - (edep[st.query(x, y)] << 1);
}
int lca(int x, int y) {
  x = ein[x], y = eout[y];
  if (x > y) {
    swap(x, y);
  }
  return tour[st.query(x, y)];
}

void dfs(int p, int fa, int d) {
  edep[++dfc] = d;
  tour[dfc] = p;
  ein[p] = dfc;
  for (auto e : G[p]) {
    if (e ^ fa) {
      dfs(e, p, d + 1);
      edep[++dfc] = d;
      tour[dfc] = p;
    }
  }
  eout[p] = dfc;
}

typedef class VirtualTree {
  public:
    int length;
    multiset<int> P;

    iter prefix(iter x) {
      return x == P.begin() ? --P.end() : --x;
    }
    iter suffix(iter x) {
      ++x;
      return x == P.end() ? P.begin() : x;
    }

    void insert(int p) {
      iter x = P.insert(p);
      iter pr = prefix(x);
      iter sf = suffix(x);
      length -= edist(*pr, *sf);
      length += edist(*pr, p);
      length += edist(p, *sf);
    }

    void remove(int p) {
      iter x = P.find(p);
      iter pr = prefix(x);
      iter sf = suffix(x);
      length -= edist(*pr, p);
      length -= edist(p, *sf);
      length += edist(*pr, *sf);
      P.erase(x);
    }

    int size() {
      return P.size();
    }
} VirtualTree;

VirtualTree _vt[N];
VirtualTree *vt[N];
vector<int> Vi[N], Vo[N];

long long ans = 0;
void merge(int x, int y) {
  if (vt[x]->size() < vt[y]->size()) {
    swap(vt[x], vt[y]);
  }
  for (auto p : vt[y]->P) {
    vt[x]->insert(p);
  }
}
void solve(int p, int fa) {
  for (auto e : G[p]) {
    if (e ^ fa) {
      solve(e, p);
      merge(p, e);
    }
  }
  for (auto x : Vi[p]) {
    vt[p]->insert(x);
  }
  ans += vt[p]->length;
  for (auto x : Vo[p]) {
    vt[p]->remove(x);
    vt[p]->remove(x);
  }  
}

int main() {
  scanf("%d%d", &n, &m);
  for (int i = 1, u, v; i < n; i++) {
    scanf("%d%d", &u, &v);
    G[u].push_back(v);
    G[v].push_back(u);
  }
  for (int i = 1; i <= n; i++) {
    vt[i] = _vt + i;
  }
  dfs(1, 0, 0);
  int* tmp = new int[(dfc + 1)];
  for (int i = 0; i <= dfc; i++) {
    tmp[i] = i;
  }
  st.init(dfc, tmp, [&] (int x, int y) {  return edep[x] < edep[y]; });
  for (int i = 1, u, v; i <= m; i++) {
    scanf("%d%d", &u, &v);
    Vi[u].push_back(ein[u]);
    Vi[u].push_back(ein[v]);
    Vi[v].push_back(ein[u]);
    Vi[v].push_back(ein[v]);
    int g = lca(u, v);
    Vo[g].push_back(ein[u]);
    Vo[g].push_back(ein[v]);
  }
  solve(1, 0);
  ans >>= 2;
  printf("%lld\n", ans);
  return 0;
} 

Problem C 浙江省选

   考虑最终能成为 rk 1 的是直接半平交。考虑求最终能成为 rk 2 的,把剩下的求半平面交,把剩下可能成为 rk 1 的判断一下它出现在凸包上的区间是否被本来是 rk 1 的覆盖。具体地来说你可以用之前是 rk 1 的直线,二分一下它覆盖了凸包上哪些区间(显然这样的区间只有常数个)。这样就能找到所有能成为 rk 2 的直线。对于 rk 更大的情况只用变更为计算被覆盖的次数就行了。

  注意坐标必须是整数,以及 long long 的运算可能会炸 long long。

Code

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

const int N = 1e5 + 5;

typedef long long ll;
typedef __int128 LL;

const ll llf = (signed long long) (~0ull >> 2);

typedef class Fraction {
  public:
    ll a, b;

    Fraction() {  }
    Fraction(ll x, ll y) : a(x), b(y) {
      if (b < 0) {
        b = -b;
        a = -a;
      }
    }

    bool operator < (Fraction x) const {
      return ((LL) a) * x.b < ((LL) b) * x.a;
    }
    bool operator <= (Fraction x) const {
      return ((LL) a) * x.b <= ((LL) b) * x.a;
    }
    bool operator == (Fraction x) const {
      return ((LL) a) * x.b == ((LL) b) * x.a;
    }

    ll floor() {
      return a < 0 ? ((a - b + 1) / b) : (a / b);
    }
    ll ceil() {
      return a < 0 ? (a / b) : ((a + b - 1) / b);
    }
} Fraction;

typedef class Line {
  public:
    ll a, b;
    int id;

    Fraction intersect(Line x) {
      assert(a != x.a);
      return Fraction(x.b - b, a - x.a);
    }

    void read() {
      scanf("%lld%lld", &a, &b);
    }
} Line;

typedef class Event {
  public:
    int op, v; // 0 : insert, 1 : cover
    Fraction p;

    Event(int op, int v, Fraction p) : op(op), v(v), p(p) { }

    bool operator < (Event b) const {
      return p == b.p ? (op == b.op ? v > b.v : op < b.op) : p < b.p;
    }
} Event;

int n, m;
Line li[N];
Fraction fl[N], fr[N];

bool ban[N];
int ans[N];
Line stk[N];
vector<Event> E;
vector<int> ncand;
vector<int> candidate;

const Fraction f0 (0, 1);

int main() {
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; i++) {
    li[i].read();
    li[i].id = i;
    ans[i] = -1;
  }
  sort(li + 1, li + n + 1, [&] (Line a, Line b) { return a.a == b.a ? a.b > b.b : a.a < b.a;  });
  for (int r = 1; r <= m; r++) {
    int tp = 0;
    ll lsa = 0;
    fill(ban + 1, ban + n + 1, false);
    for (int i = 1; i <= n; i++) {
      if (ans[li[i].id] == -1 && li[i].a != lsa) {
        lsa = li[i].a;
        while (tp >= 2 && stk[tp].intersect(li[i]) < stk[tp - 1].intersect(stk[tp]))
          tp--;
        stk[++tp] = li[i];
      } else if (li[i].a == lsa) {
        ban[i] = true;
      }
    }
    if (!tp) {
      break;
    }

    E.clear();
    int cov = 0;
    for (int i = 1; i <= n; i++) {
      if (ban[i]) {
        continue;
      }
      int l = 2, r = tp, mid;
      Line& lc = li[i];
      int id = li[i].id;
      while (l <= r) {
        mid = (l + r) >> 1;
        if (stk[mid].a < lc.a && stk[mid].intersect(stk[mid - 1]) < lc.intersect(stk[mid])) {
          l = mid + 1;
        } else {
          r = mid - 1;
        }
      }
      --l;
      if (stk[l].a >= lc.a) {
        fl[i] = Fraction(-llf, 1);
      } else {
        fl[i] = stk[l].intersect(lc);
      }

      l = 1, r = tp - 1;
      while (l <= r) {
        mid = (l + r) >> 1;
        if (stk[mid].a > lc.a && lc.intersect(stk[mid]) < stk[mid].intersect(stk[mid + 1])) {
          r = mid - 1;
        } else {
          l = mid + 1;
        }
      }
      ++r;
      if (stk[r].a <= lc.a) {
        fr[i] = Fraction(llf, 1);
      } else {
        fr[i] = lc.intersect(stk[r]);
      }
      if (fr[i] < fl[i] || fr[i] < f0) {
        continue;
      }
      if (ans[id] != -1) {
        if (fl[i] < f0) {
          cov++;
        } else {
          E.emplace_back(1, 1, fl[i]);
        }
        if (fr[i].a != llf) {
          E.emplace_back(1, -1, fr[i]);
        }
      } else {
        E.emplace_back(0, i, fl[i]);
      }
    }
    sort(E.begin(), E.end());
    Fraction lsf = f0;
    bool have_first = cov < r;
    for (auto it = E.begin(), nit = it, _it = E.end(); it != _it; it = nit) {
      while (nit != _it && (*nit).p == (*it).p) {
        auto e = *nit;
        if (!e.op) {
          candidate.push_back(e.v);
        } else {
          cov += e.v;
        }
        nit++;
      }
      auto p = (*it).p;
      if (cov < r) {
        if (!have_first) {
          have_first = true;
          lsf = p;
        }
      } else if (have_first) {
        if (p.floor() >= lsf.ceil()) {
          ncand.clear();
          for (auto x : candidate) {
            auto L = max(fl[x], lsf);
            auto R = min(fr[x], p);
            if (L.ceil() <= R.floor()) {
              ans[li[x].id] = r;
            } else if (lsf < fl[x] && p <= fr[x]) {
              ncand.push_back(x);
            }
          }
          candidate = ncand;
        }
        have_first = false;
      }
    }
    if (cov < r) {
      for (auto x : candidate) {
        auto L = max(fl[x], lsf);
        auto R = fr[x];
        if (L.ceil() <= R.floor()) {
          ans[li[x].id] = r;
        }
      }
    }
    candidate.clear();
  }
  for (int i = 1; i <= n; i++) {
    printf("%d%c", ans[i], " \n"[i == n]);
  }
  return 0;
}

posted @ 2020-05-04 19:42  阿波罗2003  阅读(365)  评论(0编辑  收藏  举报