• NOI 2019 简要题解


    从这里开始

      说好的 agc 046 呢

      去年的题真难写

    Day 1

    Problem A 回家路线

      暴力即可。 2e8 真的很稳。

      可以按开始时间排序,然后每个点上斜率优化。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 2e5 + 5;
    
    #define ll long long
    
    const ll llf = (signed ll) (~0ull >> 3);
    
    typedef class Point {
      public:
        int x;
        ll y;
        
        Point(int x = 0, ll y = 0) : x(x), y(y) { }
    } Point;
    
    Point operator - (Point a, Point b) {
      return Point(a.x - b.x, a.y - b.y);
    }
    
    ll cross(Point a, Point b) {
      return a.x * b.y - a.y * b.x;
    }
    
    typedef class ConvexHull {
      public:
        int pos;
        vector<Point> stk;
        
        ConvexHull() : pos(0) { }
       
        void insert(int _x, ll _y) {
          Point p (_x, _y);
          while (!stk.empty() && stk.back().x == _x && stk.back().y >= _y)
            stk.pop_back();
          while (stk.size() > 1u && cross(stk.back() - stk[stk.size() - 2], p - stk.back()) <= 0)
            stk.pop_back();
          stk.push_back(p);
        }
        ll query(ll k) {
          if (stk.empty()) {
            return llf;
          }
          ll ret = llf;
          for (int i = 0; i < (signed) stk.size(); i++) {
            ret = min(ret, stk[i].y - stk[i].x * k);
          }
          pos = min(pos, (signed) stk.size() - 1);
          while (pos < (signed) stk.size() - 1 && stk[pos].y - k * stk[pos].x > stk[pos + 1].y - k * stk[pos + 1].x)
            pos++;
          return stk[pos].y - k * stk[pos].x;
        }
    } ConvexHull;
    
    typedef class Route {
      public:
        int s, t, p, q;
        
        void read() {
          scanf("%d%d%d%d", &s, &t, &p, &q);
        }
        
        bool operator < (Route b) const {
          return p < b.p;
        }
    } Route;
    
    int n, m, A, B, C;
    ll f[N];
    Route R[N];
    int ord[N];
    ConvexHull con[N];
    
    int main() {
      freopen("route.in", "r", stdin);
      freopen("route.out", "w", stdout);
      scanf("%d%d%d%d%d", &n, &m, &A, &B, &C);
      for (int i = 1; i <= m; i++) {
        R[i].read();
        assert(R[i].p < R[i].q);
      }
      sort(R + 1, R + m + 1);
      R[0].t = 1, R[0].q = 0;
      for (int i = 0; i <= m; i++) {
        ord[i] = i;
      }
      sort(ord + 1, ord + m + 1, [&] (int x, int y) { return R[x].q < R[y].q; });
      ll ans = llf;
      int* po = ord, *_po = ord + m + 1;
      for (int i = 1; i <= m; i++) {
        while (po != _po && R[*po].q <= R[i].p) {
          if (f[*po] < (llf >> 1)) {
            Route &r = R[*po];
            con[r.t].insert(r.q, f[*po] + 1ll * A * r.q * r.q - 1ll * B * r.q);
          }
          po++;
        }
        Route& r = R[i];
        f[i] = con[r.s].query(2 * A * r.p);
        if (f[i] >= (llf >> 1))
          continue;
        f[i] += 1ll * A * r.p * r.p + 1ll * B * r.p + C;
        if (r.t == n) {
          ans = min(ans, f[i] + r.q);
        } 
      }
      printf("%lld
    ", ans);
      return 0;
    }

    Problem B 机器人

      显然答案是一个关于最大能选的数的不超过 $n$ 次的分段多项式。

      写一个暴力打表发现涉及到的区间的长度和最长也就 17400 左右,因为一个点可能因为 +1 被加上 $O(log n)$ 次,大概总的段数也在 $10^5$ 左右。直接用点值维护分段多项式就行了。据说直接暴力维护出系数表示的多项式也能过。

    Code 

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 305;
    const int Mod = 1e9 + 7;
    
    const int inf = (~0u >> 2);
    
    #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 x, y;
      exgcd(a, Mod, x, y);
      return x < 0 ? x + Mod : x;
    }
    
    typedef class Zi {
      public:
        int v;
         
        Zi() { }
        Zi(int v) : v(v) { }
        Zi(ll x) : v(x % Mod) {  }
          
        friend Zi operator + (Zi a, Zi b) {
          int x = a.v + b.v;
          return x >= Mod ? x - Mod : x;
        }
        friend Zi operator - (Zi a, Zi b) {
          int x = a.v -b.v;
          return x < 0 ? x + Mod : x; 
        }
        friend Zi operator * (Zi a, Zi b) {
          return 1ll * a.v * b.v;
        }
        friend Zi operator ~ (Zi a) {
          return inv(a.v);
        }
        Zi& operator += (Zi b) {
          return *this = *this + b;
        }
        Zi& operator -= (Zi b) {
          return *this = *this - b;
        }
        Zi& operator *= (Zi b) {
          return *this = *this * b;
        }
    } Zi;
    
    vector<Zi> Inv;
    vector<Zi> fac, _fac;
    
    void prepare(int n) {
      Inv.resize(n + 1);
      for (int i = 1; i <= n; i++) {
        Inv[i] = ~Zi(i);
      }
      fac.resize(n + 1);
      _fac.resize(n + 1);
      fac[0] = 1;
      for (int i = 1; i <= n; i++) {
        fac[i] = fac[i - 1] * i;
      }
      _fac[n] = ~fac[n];
      for (int i = n; i; i--) {
        _fac[i - 1] = _fac[i] * i;
      }
    }
    
    int n, S;
    
    typedef class Segment {
      public:
        int r;
        vector<Zi> f;
        
        Segment() { }
        Segment(int r, vector<Zi> f) : r(r), f(f) { }
        
        void init(Zi x) {
          f.resize(S, x);
        }
        Zi eval(Zi n) {
          static Zi L[N], R[N];
          if (n.v < S) {
            return f[n.v];
          }
          L[0] = 1;
          for (int i = 0; i < S; i++) {
            L[i + 1] = L[i] * (n - i); 
          }
          R[S] = 1;
          for (int i = S; i--; ) {
            R[i] = R[i + 1] * (n - i);
          }
          Zi ret = 0;
          for (int i = 0; i < S; i++) {
            Zi tmp = f[i] * L[i] * R[i + 1] * _fac[i] * _fac[S - 1 - i];
            if ((S - 1 - i) & 1) {
              ret -= tmp; 
            } else {
              ret += tmp;
            }
          }
          return ret;
        }
        Segment operator + (const Segment& b) {
          Segment s;
          s.init(S);
          s.r = min(r, b.r);
          for (int i = 0; i < S; i++) {
            s.f[i] = f[i] + b.f[i];
          } 
          return s;
        }
        Segment operator * (const Segment& b) {
          Segment s;
          s.init(S);
          s.r = min(r, b.r);
          for (int i = 0; i < S; i++) {
            s.f[i] = f[i] * b.f[i];
          }
          return s;
        }
        
        Segment& operator += (Zi x) {
          for (auto& y : f)
            y += x;
          return *this;
        }
        
        void pre_sum() {
          Zi sum = 0;
          for (int i = 0; i < S; i++) {
            sum += f[i];
            f[i] = sum;
          }
        }
        
        void shift() {
          r = min(r + 1, inf);
          f.insert(f.begin(), eval(Mod - 1));
          f.pop_back();
        }
    } Segment;
    
    typedef class Function {
      public:
        vector<Segment> seg;
        
        void append(Segment s) {
          seg.push_back(s);
        }
          
        void pre_sum() {
          int ls = 0;
          Zi sum = 0;
          for (auto&s : seg) {
            s.pre_sum();
            s += (sum - s.eval(ls));
            ls = s.r;
            sum = s.eval(s.r);
          }
        }
        
        Function operator * (const Function& b) {
          Function f;
          auto pl = seg.begin(), _pl = seg.end();
          auto pr = b.seg.begin(), _pr = b.seg.end();
          while (pl != _pl && pr != _pr) {
            if ((*pl).r == (*pr).r) {
              f.append(*(pl++) * *(pr++));
            } else if ((*pl).r < (*pr).r) {
              f.append(*(pl++) * *pr);
            } else {
              f.append(*pl * *(pr++));
            }
          }
          assert(pl == _pl && pr == _pr);
          return f;
        }
        Function operator + (const Function& b) {
          Function f;
          auto pl = seg.begin(), _pl = seg.end();
          auto pr = b.seg.begin(), _pr = b.seg.end();
          while (pl != _pl && pr != _pr) {
            if ((*pl).r == (*pr).r) {
              f.append(*(pl++) + *(pr++));
            } else if ((*pl).r < (*pr).r) {
              f.append(*(pl++) + *pr);
            } else {
              f.append(*pl + *(pr++));
            }
          }
          assert(pl == _pl && pr == _pr);
          return f;
        }
        
        Function& reserve(int l, int r) {
          for (int i = seg.size(); i--; ) {
            if (!i || seg[i - 1].r < r) {
              seg[i].r = r;
              seg.erase(seg.begin() + i + 1, seg.end());
              break;
            }
          }
          seg.push_back(Segment(inf, vector<Zi>(S, 0)));
          if (l == 1) return *this;
          for (int i = 0; i < (seg.size()); i++) {
            if (seg[i].r >= l) {
              seg.erase(seg.begin(), seg.begin() + i);
              break;
            }      
          }
          seg.insert(seg.begin(), Segment(l - 1, vector<Zi>(S, 0)));
          return *this;
        }
        
        Function& shift() {
          for (auto& s : seg) {
            s.shift();
          }
          seg.insert(seg.begin(), Segment(1, vector<Zi>(S, 0)));
          return *this;
        }
        
        void shrink() {
          vector<Segment> nseg;
          int ls = 0;
          for (auto& s : seg) {
            if (s.r > ls) {
              nseg.push_back(s);
              ls = s.r; 
            }        
          }
          seg = nseg;
        }
    } Function;
    
    int A[N], B[N];
    bool vis[N][N];
    Function f0, fe, f[N][N];
    
    int Abs(int x) {
      return x < 0 ? -x : x;
    } 
    
    Function solve(int l, int r) {
      if (l > r) {
        return fe;
      }
      if (vis[l][r]) {
        return f[l][r];
      }
      vis[l][r] = true;
      Function& F = f[l][r];
      F = f0;
      for (int i = l; i <= r; i++) {
        if (Abs((i - l) - (r - i)) <= 2) {
          F = F + (solve(l, i - 1) * ((i + 1 <= r) ? solve(i + 1, r).shift() : fe)).reserve(A[i], B[i]);
        }
      }
      F.shrink();
      F.pre_sum();
      return F;
    }
    
    int main() {
      freopen("robot.in", "r", stdin);
      freopen("robot.out", "w", stdout);
      scanf("%d", &n);
      S = n + 2;
      prepare(S + 3);
      for (int i = 1; i <= n; i++) {
        scanf("%d%d", A + i, B + i); 
      }
      f0.append(Segment(inf, vector<Zi>(S, 0)));
      fe.append(Segment(inf, vector<Zi>(S, 1)));
      Function fans = solve(1, n);
      Zi ans = fans.seg.back().eval(inf);
      printf("%d
    ", ans.v);
      return 0;
    }

    Problem C 序列

      开大数组可以得到比预估更高的分数,学到了。 

      有一个比较好想的建图,然而大概平方版本跑不过 2e3,模拟费用流跑不过 3e5。 d1 t3 还卡常真有毒。

      考虑要求每次选择选择一个 $x$ 也选择一个 $y$,这样能够很好地限制它们数量相等。

      建两列点,$A_i$ 向 $B_i$ 连边,再建一个一对点 $x, x'$,连边 $(x, x', K - L , 0)$,用于限制选择不同的一对的数量。然后 $A_i$ 向 $x$ 连边,$x'$ 向 $B_i$  连边。$S$ 向 $A_i$ 连边,$B_i$ 向 $T$ 连边。

      考虑怎么费用流:

    • 如果 $x ightarrow$ 未满流了,那么无论如何都相当于左边选择最大,右边再选择一个最大的,然后再判断它的流量会不会增加。
    • 否则有 $4$ 种情况
      • $S ightarrow A_i ightarrow B_i ightarrow T$
      • $S ightarrow A_i ightarrow x ightarrow A_j ightarrow B_j ightarrow T$
      • $S ightarrow A_i ightarrow B_i ightarrow x' ightarrow B_j ightarrow T$
      • $S ightarrow A_i ightarrow B_i ightarrow x'  ightarrow x  ightarrow  A_j  ightarrow B_j  ightarrow  T$

      然后瞎维护一下就行了

    Code

    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    typedef class Input {
    	protected:
    		const static int limit = 65536;
    		FILE* file; 
    
    		int ss, st;
    		char buf[limit];
    	public:
    		
    		Input() : file(NULL)	{	};
    		Input(FILE* file) : file(file) {	}
    
    		void open(FILE *file) {
    			this->file = file;
    		}
    
    		void open(const char* filename) {
    			file = fopen(filename, "r");
    		}
    
    		char pick() {
    			if (ss == st)
    				st = fread(buf, 1, limit, file), ss = 0;//, cerr << "str: " << buf << "ed " << st << endl;
    			return buf[ss++];
    		}
    } Input;
    
    #define digit(_x) ((_x) >= '0' && (_x) <= '9')
    
    Input& operator >> (Input& in, unsigned& u) {
    	char x;
    	while (~(x = in.pick()) && !digit(x));
    	for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
    	return in;
    }
    
    Input& operator >> (Input& in, unsigned long long& u) {
    	char x;
    	while (~(x = in.pick()) && !digit(x));
    	for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
    	return in;
    }
    
    Input& operator >> (Input& in, int& u) {
    	char x;
    	while (~(x = in.pick()) && !digit(x) && x != '-');
    	int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
    	for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
    	u *= aflag;
    	return in;
    }
    
    Input& operator >> (Input& in, long long& u) {
    	char x;
    	while (~(x = in.pick()) && !digit(x) && x != '-');
    	int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
    	for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
    	u *= aflag;
    	return in;
    }
    
    Input& operator >> (Input& in, double& u) {
    	char x;
    	while (~(x = in.pick()) && !digit(x) && x != '-');
    	int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
    	for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
    	if (x == '.') {
    		double dec = 1;
    		for ( ; ~(x = in.pick()) && digit(x); u = u + (dec *= 0.1) * (x - '0'));
    	}
    	u *= aflag;
    	return in;
    }
    
    Input& operator >> (Input& in, char* str) {
    	char x;
    	while (~(x = in.pick()) && x != '
    ' && x != ' ')
    		*(str++) = x;
    	*str = 0;
    	return in;
    }
    
    Input in (fopen("sequence.in", "r"));
    
    typedef class Output {
    	protected:
    		const static int Limit = 65536;
    		char *tp, *ed;
    		char buf[Limit];
    		FILE* file;
    		int precision;
    
    		void flush() {
    			fwrite(buf, 1, tp - buf, file);
    			fflush(file);
    			tp = buf;
    		}
    
    	public:
    
    		Output() {	}
    		Output(FILE* file) : tp(buf), ed(buf + Limit), file(file), precision(6) {	}
    		Output(const char *str) : tp(buf), ed(buf + Limit), precision(6) {
    			file = fopen(str, "w");
    		}
    		~Output() {
    			flush();
    		}
    
    		void put(char x) {
    			if (tp == ed)
    				flush();
    			*(tp++) = x;
    		}
    
    		int get_precision() {
    			return precision;
    		}
    		void set_percision(int x) {
    			precision = x;
    		}
    } Output;
    
    Output& operator << (Output& out, int x) {
    	static char buf[35];
    	static char * const lim = buf + 34;
    	if (!x)
    		out.put('0');
    	else {
    		if (x < 0)
    			out.put('-'), x = -x;
    		char *tp = lim;
    		for ( ; x; *(--tp) = x % 10, x /= 10);
    		for ( ; tp != lim; out.put(*(tp++) + '0'));
    	}
    	return out;
    }
    
    Output& operator << (Output& out, long long x) {
    	static char buf[36];
    	static char * const lim = buf + 34;
    	if (!x)
    		out.put('0');
    	else {
    		if (x < 0)
    			out.put('-'), x = -x;
    		char *tp = lim;
    		for ( ; x; *(--tp) = x % 10, x /= 10);
    		for ( ; tp != lim; out.put(*(tp++) + '0'));
    	}
    	return out;
    }
    
    Output& operator << (Output& out, unsigned x) {
    	static char buf[35];
    	static char * const lim = buf + 34;
    	if (!x)
    		out.put('0');
    	else {
    		char *tp = lim;
    		for ( ; x; *(--tp) = x % 10, x /= 10);
    		for ( ; tp != lim; out.put(*(tp++) + '0'));
    	}
    	return out;
    }
    
    Output& operator << (Output& out, char x)  {
    	out.put(x);
    	return out;
    }
    
    Output& operator << (Output& out, const char* str) {
    	for ( ; *str; out.put(*(str++)));
    	return out;
    }
    
    Output& operator << (Output& out, double x) {
    	int y = x;
    	x -= y;
    	out << y << '.';
    	for (int i = out.get_precision(); i; i--, y = x * 10, x = x * 10 - y, out.put(y + '0'));
    	return out;
    }
    
    Output out ("sequence.out");
    
    #define pii pair<int, int>
    #define ll long long
    
    const int inf = (signed) (~0u >> 2);
    
    typedef class ZKW {
      public:
        int M;
        pii mx[524288];
        
        void init(int n, pii* w) {
          for (M = 1; M < n; M <<= 1);
          for (int i = 0; i < n; i++) {
            mx[i + M] = w[i];
          }
          for (int i = n; i < M; i++) {
            mx[i + M] = pii(-inf, 0);
          }
          for (int i = M; --i; push_up(i));
        }
        
        void push_up(int p) {
          mx[p] = max(mx[p << 1], mx[p << 1 | 1]);
        }
        void upd(int p, int vi) {
          p += M;
          mx[p] = pii(vi, p - M);
          for ( ; p >>= 1; push_up(p));
        }
        int qry() {
          return mx[1].first;
        }
        int qryi() {
          return mx[1].second;
        }
    } ZKW;
    
    const int N = 2e5 + 5;
    
    int T, n, K, L;
    int cnt;
    int a[N], b[N];
    ZKW zs, za, zb, _za, _zb;
    bool ina[N], inb[N];
    
    void seclecta(int p) {
      ina[p] = true;
      cnt -= ina[p] && inb[p];
      zs.upd(p, -inf);
      za.upd(p, -inf);
      _za.upd(p, -inf);
      if (!inb[p])
        _zb.upd(p, b[p]);
    }
    void seclectb(int p) {
      inb[p] = true;
      cnt -= ina[p] && inb[p];
      zs.upd(p, -inf);
      zb.upd(p, -inf);
      _zb.upd(p, -inf);
      if (!ina[p])
        _za.upd(p, a[p]);
    }
    
    void solve() {
      static pii tmp[N];
      cnt = 0;
      in >> n >> K >> L;
      for (int i = 0; i < n; i++) {
        in >> a[i];
      }
      for (int i = 0; i < n; i++) {
        in >> b[i];
      }
      for (int i = 0; i < n; i++)
        tmp[i] = pii(a[i] + b[i], i);
      zs.init(n, tmp);
      for (int i = 0; i < n; i++)
        tmp[i] = pii(a[i], i);
      za.init(n, tmp);
      for (int i = 0; i < n; i++)
        tmp[i] = pii(b[i], i);
      zb.init(n, tmp);
      for (int i = 0; i < n; i++)
        tmp[i] = pii(-inf, i);
      _za.init(n, tmp);
      _zb.init(n, tmp);
      fill(ina, ina + n, false);
      fill(inb, inb + n, false);
      for (int i = 1; i <= K; i++) {
        if (cnt == K - L) {
          int swpa = _za.qry() + zb.qry();
          int swpb = za.qry() + _zb.qry();
          int mx = zs.qry(), id = 0, q;
          if ((q = _za.qry() + _zb.qry()) > mx)
            id = 1, mx = q;
          if ((q = swpa) > mx)
            id = 2, mx = q;
          if ((q = swpb) > mx)
            id = 3, mx = q;
          if (!id) {
            q = zs.qryi();
            seclecta(q);
            seclectb(q);      
          } else if (id == 1) {
            seclecta(_za.qryi());
            seclectb(_zb.qryi());
          } else if (id == 2) {
            seclecta(_za.qryi());
            seclectb(zb.qryi());
          } else {
            seclecta(za.qryi());
            seclectb(_zb.qryi());
          }
        } else {
          seclecta(za.qryi());
          seclectb(zb.qryi());
        }
        ++cnt;
      }
      ll ans = 0;
      for (int i = 0; i < n; i++) {
        ans += ina[i] * a[i];
        ans += inb[i] * b[i];
      }
      out << ans << '
    ';
    }
    
    int main() {
      in >> T;
      while (T--) {
        solve();
      }
      return 0;
    }

    Day 2

    Problem A 弹跳 

      考虑 dijkstra 的过程,每次我们加入一条边。一条边更新一个点后,一个点就不可能再被更新了。因此我们只用查询一个矩形内的所有点然后把它们删除。

      可以用线段树套 set 或者 KDtree 维护。

      时间复杂度 $O(nlog^2 n + mlog n)$。

    Code

    /**
     * loj
     * Problem#3159
     * Accepted
     * Time: 6467ms
     * Memory: 51568k
     */
    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    const int N = 7e4 + 5;
    const int M = 150005;
    
    typedef class Node {
    	public:
    		int eid, dis;
    
    		Node() {	}
    		Node(int eid, int dis) : eid(eid), dis(dis) {	}
    
    		boolean operator < (Node b) const {
    			return dis > b.dis;
    		}
    } Node;
    
    #define pii pair<int, int>
    
    int n, m, w, h;
    int f[N];
    boolean vis[N];
    vector<int> G[N];
    set<pii> S[N << 2];
    priority_queue<Node> Q;
    
    int xl[M], xr[M], yl[M], yr[M], W[M];
    
    void insert(int p, int l, int r, int x, int y, int id) {
    	S[p].insert(pii(y, id));
    	if (l == r) return;
    	int mid = (l + r) >> 1;
    	if (x <= mid) {
    		insert(p << 1, l, mid, x, y, id);
    	} else {
    		insert(p << 1 | 1, mid + 1, r, x, y, id);
    	}
    }
    
    void del(int p, int l, int r, int x1, int x2, int y1, int y2, int w) {
    	if (l == x1 && r == x2) {
    		set<pii>::iterator it = S[p].lower_bound(pii(y1, 0));
    		while (it != S[p].end() && (*it).first <= y2) {
    			int id = (*it).second;
    			if (!vis[id]) {
    				vis[id] = true;
    				f[id] = w;
    				for (auto eid : G[id]) {
    					Q.push(Node(eid, f[id] + W[eid]));
    				}
    			}
    			it = S[p].erase(it);
    		}
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if (x2 <= mid) {
    		del(p << 1, l, mid, x1, x2, y1, y2, w);
    	} else if (x1 > mid) {
    		del(p << 1 | 1, mid + 1, r, x1, x2, y1, y2, w);
    	} else {
    		del(p << 1, l, mid, x1, mid, y1, y2, w);
    		del(p << 1 | 1, mid + 1, r, mid + 1, x2, y1, y2, w);
    	}
    }
    
    int main() {
    	freopen("jump.in", "r", stdin);
    	freopen("jump.out", "w", stdout);
    	scanf("%d%d%d%d", &n, &m, &w, &h);
    	for (int i = 1, x, y; i <= n; i++) {
    		scanf("%d%d", &x, &y);
    		if (i ^ 1) {
    			insert(1, 1, w, x, y, i);
    		}
    	}
    	for (int i = 1, p; i <= m; i++) {
    		scanf("%d%d%d%d%d%d", &p, W + i, xl + i, xr + i, yl + i, yr + i);
    		G[p].push_back(i);
    	}
    	for (auto e : G[1]) {
    		Q.push(Node(e, W[e]));
    	}
    	while (!Q.empty()) {
    		int e = Q.top().eid;
    		int d = Q.top().dis;
    		Q.pop();
    		del(1, 1, w, xl[e], xr[e], yl[e], yr[e], d);
    	}
    	for (int i = 2; i <= n; i++) {
    		printf("%d
    ", f[i]);
    	}
    	return 0;
    }

    Problem B 斗主地

      开始听说看过具体数学就会,后来听说打个表也没了,当时我就不一样,既没读过书,也不会打表

      考虑直接计算 $E_i$ 贡献到 $E'_j$ 的系数。不妨先考虑 $i leqslant A$ 的情形

    $$
    egin{align}
    inom{n - j}{A - i} frac{A^{underline{A-i+1}}(n-A)^{underline{n - j - (A- i)}}}{n^{underline{n - j + 1}}} &= inom{n}{A}^{-1}inom{n-j}{A- i}inom{j-1}{i-1}
    end{align}
    $$

      然后 $E'_j$ 会对 $i leqslant A$ 的 $E_i$ 乘上这个系数进行求和。

      先考虑 $E_i = i$ 的时候,提出 $inom{n}{A}^{-1}$, 考虑这个式子的组合意义:从 $n$ 个球选出 $A$ 个球,第 $j$ 个球一定被选择,如果是第 $i$ 个被选择的球,贡献为 $i$。

      考虑枚举被选择的球,不难得到它等于 $inom{n - 1}{A - 1} + (j - 1)inom{n - 2}{A - 2}$。

      对于 $i > A$ 的时候是类似的做法。

      容易发现 $E'_j$ 是关于 $j$ 的一次多项式。

      当 $type = 2$ 的时候是关于 $j$ 的二次多项式。

      可以暴力维护,也可以插值。

      插值竞赛(确信

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    #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;
        }
    };
    
    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;
    
    int n, m, type, q;
    
    vector<Zi> fac, _fac;
    
    void prepare(int n) {
      fac.resize(n + 1);
      _fac.resize(n + 1);
      fac[0] = 1;
      for (int i = 1; i <= n; i++) {
        fac[i] = fac[i - 1] * i;
      }
      _fac[n] = ~fac[n];
      for (int i = n; i; i--) {
        _fac[i - 1] = _fac[i] * i;
      }
    }
    Zi comb(int n, int m) {
      assert(n >= 0 && m >= 0);
      return n < m ? 0 : fac[n] * _fac[m] * _fac[n - m];
    }
    Zi _comb(int n, int m) {
      return _fac[n] * fac[m] * fac[n - m];
    }
    
    typedef vector<Zi> vec;
    
    vec operator * (vec a, Zi k) {
      for (auto& x : a)
        x *= k;
      return a;
    }
    vec operator + (vec a, vec b) {
      if (a.size() < b.size())
        a.resize(b.size());
      for (int i = 0; i < (signed) b.size(); i++)
        a[i] += b[i];
      return a;
    }
    
    Zi eval(int A, int c) {
      return A - 1 < c ? 0 : comb(n - 1 - c, A - 1 - c);
    }
    
    vec get1(int A) {
      vec v (2);
      v[0] = eval(n - A, 0) * A;
      v[1] = eval(A, 1) + eval(n - A, 1);
      return v * _comb(n, A);
    }
    vec get2(int A) {
      vec v (3);
      v[0] = eval(n - A, 0) * A * (A - 1) * ((Mod + 1) >> 1);
      v[1] = eval(n - A, 1) * A;
      v[2] = eval(A, 2) + eval(n - A, 2);
      return v * _comb(n, A);
    }
    
    int main() {
      freopen("landlords.in", "r", stdin);
      freopen("landlords.out", "w", stdout);
      scanf("%d%d%d", &n, &m, &type);
      prepare(n + 1);
      vec f, g;
      if (type == 1) {
        f = {1, 1};
      } else {
        f = {1, 3, 2};
      }
      for (int i = 1, a; i <= m; i++) {
        scanf("%d", &a);
        g = vec {f[0]};
        g = g + get1(a) * f[1];
        if (type == 2)
          g = g + get2(a) * f[2];
        f = g;
      }
      scanf("%d", &q);
      Zi x;
      while (q--) {
        scanf("%d", &x.v);
        Zi ans = f[0] + f[1] * (x - 1);
        if (type == 2)
          ans += f[2] * (x - 1) * (x - 2) * ((Mod + 1) >> 1);
        printf("%d
    ", ans.v);
      }
      return 0;
    }

    Problem C I 君的探险

      A:不难得到每个点周围一圈的异或和,做完了。

      B:直接整体二分。

      对于树的部分分考虑可以假设一个点是叶子,然后用 2 次修改 1 次询问检查它是不是,因此我们可以判断一个点是不是叶子。然后每次剥叶子就可以了。

      对于一般图的情形,考虑随机一个排列,然后套用 B 的整体二分做法,如果一个点向前连了奇数条边,那么一定能找到一条边,否则可能能找到。

      不难证明最坏情况下,期望能减少 $frac{t}{3}$ 条边,其中 $t$ 是度数非 0 的点的数量。因此当有孤立点的时候复杂度有点假,需要删除孤立点。

      否则的话,可以花费 $O(nlog n)$ 的代价,期望减少 $frac{n}{3}$ 的边。因此期望的操作次数和复杂度均是 $O(mlog n)$。

      至于初始孤立点我好像只会一些常数比较劣的 $O(nlog n)$ 随机化做法,加了估计操作次数会超,不过数据好像没有卡不处理初始孤立点的情况。

    Code

    #include <bits/stdc++.h>
    #include "explore.h"
    using namespace std;
    
    int N, M;
    
    namespace brute {
      
      vector<int> old;
      
      void solve() {
        old.resize(N, 0);
        for (int i = 0; i < N - 1; i++) {
          modify(i);
          for (int j = i + 1; j < N; j++) {
            int x = query(j);
            if (x ^ old[j]) {
              report(i, j);
              old[j] = x;
            }
          }
        }
      }
      
    }
    
    namespace subtaskA {
    
    vector<int> v;
    vector<int> old;
    
    void solve() {
      v.assign(N, 0);
      old.assign(N, 0);
      for (int b = 1; b < N; b <<= 1) {
        for (int i = 0; i < N; i++) {
          if (i & b) {
            modify(i);
          }
        }
        for (int i = 0; i < N; i++) {
          int x = query(i);
          if (old[i] ^ x) {
            v[i] |= b;
            old[i] = x;
          }
        }
      }
      for (int i = 0; i < N; i++) {
        if ((v[i] ^ i) < i) {
          report(v[i] ^ i, i);
        }
      }
    }
    
    }
    
    namespace subtaskB {
    
    void dividing(int l, int r, vector<int> p)  {
      if (l == r) {
        for (auto x : p) {
          report(l, x);
        }
        return;
      }
      if (p.empty()) {
        return;
      }
      int mid = (l + r) >> 1;
      for (int i = l; i <= mid; i++) {
        modify(i);
      }
      vector<int> vl, vr;
      for (auto x : p) {
        if (x <= mid || query(x)) {
          vl.push_back(x); 
        } else {
          vr.push_back(x);
        }
      }
      for (int i = l; i <= mid; i++) {
        modify(i);
      }
      dividing(l, mid, vl);
      dividing(mid + 1, r, vr);
    }
    
    void solve() {
      vector<int> p (N - 1);
      for (int i = 1; i < N; i++)
        p[i - 1] = i;
      dividing(0, N - 1, p);
    }
    
    }
    
    namespace general {
    
    vector<int> P;
    vector<int> on;
    vector<int> tg;
    vector<bool> exist;
    vector<vector<int>> G;
    
    void answer(int x, int y) {
      report(x, y);
      G[x].push_back(y);
      G[y].push_back(x);
      M--;
      if (check(x)) {
        exist[x] = false;
      }
      if (check(y)) {
        exist[y] = false;
      }
    }
    void upd(int p) {
      modify(p);
      on[p] ^= 1; 
    }
    bool qry(int p) {
      bool ret = query(p);
      for (auto x : G[p]) {
        ret ^= on[x];
      }
      return ret ^ tg[p];
    }
    
    void light_on(int l, int r) {
      static int ql = 0, qr = -1;
      if (l < 0) {
        for (int i = ql; i <= qr; i++) {
          upd(P[i]);
        }
        ql = 0, qr = -1;
        return;
      }
      for (int i = ql; i <= qr; i++) {
        if (i < l || i > r) {
          upd(P[i]);
        }
      }
      for (int i = l; i <= r; i++) {
        if (!on[P[i]]) {
          upd(P[i]);  
        }
      }
      ql = l, qr = r;
    }
    
    void dividing(int l, int r, vector<int> S) {
      if (S.empty()) {
        return;
      }
      if (l == r) {
        int p = P[l];
        light_on(l, l);
        for (auto x : S) {
          if (P[x] != p && qry(P[x])) {
            answer(p, P[x]);
          }
        }
        return;
      }
      int mid = (l + r) >> 1;
      light_on(l, mid);
      vector<int> sl, sr;
      for (auto x : S) {
        if (x <= mid || qry(P[x])) {
          sl.push_back(x);
        } else {
          sr.push_back(x);
        }
      }
      dividing(l, mid, sl);
      dividing(mid + 1, r, sr);
    }
    
    void solve() {
      P.resize(N);
      tg.assign(N, 0);
      on.assign(N, false);
      exist.assign(N, true);
      G.assign(N, vector<int>());
      for (int i = 0; i < N; i++)
        P[i] = i;
      vector<int> P0 = P;
      while (M) {
        light_on(-1, 0);
        vector<int> tmp;
        for (auto x : P) {
          if (exist[x]) {
            tmp.push_back(x);
          }
        }
        P = tmp;
        P0.resize(P.size());
        random_shuffle(P.begin(), P.end());
    //    cerr << P.size() << " " << " " << M << '
    ';
        dividing(0, (signed) P.size() - 1, P0);
      }
    }
    
    }
    
    void explore(int N, int M) {
      srand(time(NULL));
    //  srand(233u);
      ::N = N;
      ::M = M;
      if (N <= 500) {
        brute::solve();
      } else if (N % 10 == 8) {
        subtaskA::solve();
      } else if (N % 10 == 7) {
        subtaskB::solve();
      } else {
        general::solve(); 
      }
    }

  • 相关阅读:
    input文本框加入xwebkitspeech实现语音输入功能
    获取textarea的光标位置
    初学者使用Application Cache指南
    一个封装了localStorage的增删改查的方法
    video from html5
    Asynchronous Method Invocation
    consume an asp.net webservice(upload a file to server) from java via soap
    INFO:VB/VBA (Long) 的转换自动化错误
    : 使用SAAJ发送和接收SOAP消息
    how to design a hardware service use .net remoting
  • 原文地址:https://www.cnblogs.com/yyf0309/p/noi2019.html
Copyright © 2020-2023  润新知