zoukankan      html  css  js  c++  java
  • 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 << "
    ";
          for (int i = 0; i <= 2; i++) {
            for (int j = 0; j <= 2; j++) {
              cerr << f[1][i][j] << " ";
            }
            cerr << '
    ';
          }
          cerr << '
    ';
          cerr << "----
    ";
        }
    } 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
    ", 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
    ", 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) ? ('
    ') : ' ');
      } 
      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)} ight )'mid_{x = 1}$。你注意到分子分母都包含 $frac{1}{1 - x}$,分子分母同乘 $(1 - x)$ 再套用求导的公式。 然后手算一下 $left ( frac{1 - x}{1 - kx} ight )'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
    ", 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
    ", 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], " 
    "[i == n]);
      }
      return 0;
    }

  • 相关阅读:
    Hibernate课程 初探一对多映射3-1 单向多对一简介
    Hibernate课程 初探一对多映射2-8 set元素属性
    Hibernate课程 初探一对多映射2-7 测试-修改和删除学生信息
    vue.js源码学习分享(二)
    vue.js源码学习分享(一)
    用百度地图做了一个输入地址查询经纬度的小例子
    今天碰到的400错误
    ajax同步
    json键的不能像值一样拼写的问题
    日期格式化
  • 原文地址:https://www.cnblogs.com/yyf0309/p/zjoi2019.html
Copyright © 2011-2022 走看看