zoukankan      html  css  js  c++  java
  • 「九省联考 2018」秘密袭击(差分+生成函数+拉格朗日插值+整体dp+线段树合并):

    https://loj.ac/problem/2473

    求恰好第k大的权值显然不可做,差分一下,转换成:

    枚举(W),求第k大(>=W)的方案数,发现对所有这样的(W)的方案数加起来就是答案。

    这样就相当于权值(>=W)选了有1的贡献,权值(<W)选了没有贡献。

    要求选的1的个数(>=k)的方案数。

    显然这个可以用暴力树形依赖背包在(O(n^2))的时间解决,总复杂度(O(n^3)),据说就能跑过数据了。

    考虑用整体dp的思想去解决这个问题:

    (F[i][j])表示(i)为根子树,(W=j),得到的方案数的OGF。

    注意到(F[i][j])只有(siz[i])段不同的OGF。

    (F)的转移就是枚举(i)的一个子节点,然后把相同(j)对应的OGF乘起来,如果用线段树合并去实现,合并OGF时套个FFT,时间复杂度:(O(n^2~log^2n)),明显只会更慢。

    考虑把(F[i][j])转换成一个(0-n)的点值序列,这样转移就是(O(n))的,最后再(O(n^2)) 拉格朗日插值得到(x^{k..n})的系数和就行了,可以把点值的操作挪到最外层以节省空间。

    线段树合并没有那么简单,在这个特殊的线段树上,一个点没有子节点,说明它代表的区间的值时相同的。

    假设现在(merge(i,j)),当(i,j)有一方没有子节点时,就要停下来,直接操作,才能保证复杂度。

    经过讨论可以发现需要维护:

    区间赋值(初始化),区间乘、区间加,区间求和

    写就完了。

    Code:


    #include<bits/stdc++.h>
    #define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
    #define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
    #define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
    #define ll long long
    #define pp printf
    #define hh pp("
    ")
    using namespace std;
    
    const int mo = 64123;
    
    ll ksm(ll x, ll y) {
    	ll s = 1;
    	for(; y; y /= 2, x = x * x % mo)
    		if(y & 1) s = s * x % mo;
    	return s;
    }
    
    const int N = 1705;
    
    int n, k, w, m;
    int d[N], x, y;
    
    #define V vector<int>
    #define pb push_back
    #define si size()
    
    V e[N];
    
    void Init() {
    	scanf("%d %d %d", &n, &k, &w);
    	fo(i, 1, n) scanf("%d", &d[i]);
    	fo(i, 1, n - 1) {
    		scanf("%d %d", &x, &y);
    		e[x].pb(y); e[y].pb(x);
    	}
    }
    
    int fa[N], rt[N];
    
    #define i0 t[i].l
    #define i1 t[i].r
    
    struct P {
    	ll x, y, s;
    	P(ll _x = 0, ll _y = 0, ll _s = 0) {
    		x = _x, y = _y, s = _s;
    	}
    };
    
    P operator * (P a, P b) {
    	return P(a.x * b.x % mo, (a.y * b.x + b.y) % mo, a.s);
    }
    
    struct tree {
    	int l, r;
    	P v;
    } t[N * 55]; int tt;
    
    int pl, pr;
    
    int W;
    
    void cl() {
    	fo(i, 1, n) rt[i] = 0;
    	fo(i, 1, tt) t[i].l = t[i].r = 0, t[i].v = P(1, 0, 0);
    	tt = 0;
    }
    
    void down(int i, int x, int y) {
    	int m = x + y >> 1;
    	if(i0) {
    		t[i0].v = t[i0].v * t[i].v;
    		t[i1].v = t[i1].v * t[i].v;
    		
    		t[i0].v.s = (t[i0].v.s * t[i].v.x + t[i].v.y * (m - x + 1)) % mo;
    		t[i1].v.s = (t[i1].v.s * t[i].v.x + t[i].v.y * (y - m)) % mo;
    		
    		t[i].v = P(1, 0);
    	} else {
    		t[i0 = ++ tt].v = t[i].v;
    		t[i1 = ++ tt].v = t[i].v;
    		
    		t[i0].v.s = t[i].v.y * (m - x + 1) % mo;
    		t[i1].v.s = t[i].v.y * (y - m) % mo;
    		
    		t[i].v = P(1, 0);
    	}
    }
    
    void upd(int i) {
    	t[i].v.s = (t[i0].v.s + t[i1].v.s) % mo;
    }
    
    void bt(int &i, int x, int y) {
    	if(!i) i = ++ tt;
    	t[i].v = P(1, 0, 0);
    	if(y <= pl || x > pl) {
    		t[i].v = P(1, y <= pl ? W : 1, 0);
    		t[i].v.s = t[i].v.y * (y - x + 1) % mo;
    		return;
    	}
    	int m = x + y >> 1;
    	bt(i0, x, m); bt(i1, m + 1, y);
    	upd(i);
    }
    
    int mer(int i, int j, int x, int y) {
    	if(!t[j].l) {
    		ll v2 = t[j].v.y + 1;
    		t[i].v.x = t[i].v.x * v2 % mo;
    		t[i].v.y = t[i].v.y * v2 % mo;
    		t[i].v.s = t[i].v.s * v2 % mo;
    		return i;
    	}
    	if(!t[i].l) {
    		ll v2 = t[i].v.y;
    		t[j].v.y ++;
    		t[j].v.s += y - x + 1;
    		t[j].v.x = t[j].v.x * v2 % mo;
    		t[j].v.y = t[j].v.y * v2 % mo;
    		t[j].v.s = t[j].v.s * v2 % mo;
    		return j;
    	}
    	int m = x + y >> 1;
    	down(i, x, y); down(j, x, y);
    	i0 = mer(i0, t[j].l, x, m);
    	i1 = mer(i1, t[j].r, m + 1, y);
    	upd(i);
    	return i;
    }
    
    ll ans[N];
    
    void dg(int x) {
    	pl = pr = d[x];
    	bt(rt[x], 1, m);
    	ff(_y, 0, e[x].si) {
    		int y = e[x][_y];
    		if(y == fa[x]) continue;
    		fa[y] = x;
    		
    		dg(y);
    		
    		rt[x] = mer(rt[x], rt[y], 1, m);
    	}
    	ans[W] = (ans[W] + t[rt[x]].v.s) % mo;
    }
    
    ll f[N], g[N], h[N];
    
    ll inv[N];
    
    void End() {
    	fo(i, 0, n + 1) inv[i] = ksm(i, mo - 2);
    	f[0] = 1;
    	fo(i, 0, n) {
    		fd(j, i + 1, 0) f[j] = (f[j] * -i + (j ? f[j - 1] : 0)) % mo;
    	}
    	ll sum = 0;
    	fo(i, 0, n) {
    		fo(j, 0, n + 1) g[j] = f[j];
    		fd(j, n + 1, 1) {
    			h[j - 1] = g[j];
    			g[j - 1] = (g[j - 1] + i * g[j]) % mo;
    		}
    		ll xs = ans[i];
    		fo(j, 0, n) if(i != j) {
    			int x = i - j;
    			xs = xs * (x > 0 ? inv[x] : -inv[-x]) % mo;
    		}
    		fo(j, k, n) sum = (sum + xs * h[j]) % mo;
    	}
    	sum = (sum % mo + mo) % mo;
    	pp("%lld
    ", sum);
    }
    
    int main() {
    	Init();
    	m = 1666; //ggggggggggggggggggggggggggggggg
    	for(W = 0; W <= n; W ++) {
    		cl();
    		dg(1);
    	}
    	End();
    }
    
  • 相关阅读:
    网页跳转
    http报文简要分析
    【转】google hacking 谷歌搜索方法简介
    python中的切片问题
    Deepfakes教程及各个换脸软件下载
    PHP的虚拟域名的配置
    composer windows安装,使用新手入门[转]
    通过路由器的IP映射来解决,两个不同IP地址的PC机之间的从LAN口到WAN口的单向通讯问题
    [转] 如何把书上的字弄到电脑
    关于快排与随机化快排
  • 原文地址:https://www.cnblogs.com/coldchair/p/12597065.html
Copyright © 2011-2022 走看看