• @一句话题解


    topcoder - SRM765Round1 D1L3:只有单位代价最大的才可能不全选完,所以按单位代价排序然后从小到大作个背包即可。

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    class SteelMill{
    	private:
    		#define MAXN 100
    		#define MAXM 1000000
    		#define INF (1LL<<60)
    		struct node{
    			int siz; ll spc, kgc;
    			node(int _s=0, int _sc=0, int _kc=0) : siz(_s), spc(_sc), kgc(_kc) {}
    			friend bool operator < (node a, node b) {
    				return a.kgc < b.kgc;
    			}
    		}a[MAXN + 5];
    		ll f[MAXM + 5]; int n;
    	public:
    		ll cheapest(int goal, vector<int>shipmentCost, vector<int>shipmentSize, vector<int>costPerKg) {
    			n = shipmentCost.size();
    			for(int i=0;i<n;i++)
    				a[i] = node(shipmentSize[i], shipmentCost[i], costPerKg[i]);
    			ll ans = INF; sort(a, a + n); f[0] = 0;
    			for(int i=1;i<goal;i++) f[i] = INF;
    			for(int i=0;i<n;i++) {
    				ll c = a[i].spc + a[i].kgc * a[i].siz, t = a[i].spc;
    				for(int j=goal-1;j>=goal-a[i].siz&&j>=0;j--)
    					t += a[i].kgc, ans = min(ans, f[j] + t);
    				for(int j=goal-1;j>=a[i].siz;j--)
    					f[j] = min(f[j], f[j-a[i].siz] + c);
    			}
    			return ans;
    		}
    };
    

    topcoder - TCO19JapanRegionalRound D1L2:类比卡特兰数看成 H*A 的方格恰好 C 次穿过 y = x 这条线,然后直接 dp[H][A][C],转移枚举下一次在哪里穿过然后乘个卡特兰数。|H-A|总为定值所以记忆化搜索。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    class ChangesInTheLead{
    	#define MOD 1000000007
    	#define MAXN 250
    	public :
    	int c[2*MAXN + 5][2*MAXN + 5], f[MAXN + 5][MAXN + 5];
    	void init() {
    		for(int i=0;i<=2*MAXN;i++) {
    			c[i][0] = 1;
    			for(int j=1;j<=i;j++)
    				c[i][j] = (c[i-1][j] + c[i-1][j-1]) % MOD;
    		}
    		for(int i=0;i<=MAXN;i++) {
    			for(int j=0;j<=i;j++)
    				f[i][j] = (c[i+j][i] + MOD - c[i+j][i+1]) % MOD;
    		}
    		f[0][0] = 0;
    	}
    	int dp[MAXN + 5][MAXN + 5][MAXN + 5];
    	int get(int A, int B, int C) {
    		if( dp[A][B][C] != -1 ) return dp[A][B][C];
    		if( C == 0 ) return dp[A][B][C] = f[A][B];
    		dp[A][B][C] = 0;
    		for(int i=1;i<=A&&i<=B;i++)
    			dp[A][B][C] = (dp[A][B][C] + 1LL*get(B - i, A - i, C - 1)*f[i][i]%MOD) % MOD;
    		return dp[A][B][C];
    	}
    	int count(int H, int A, int C) {
    		init(); memset(dp, -1, sizeof dp);
    		if( H == 0 && A == 0 && C == 0 ) return 1;
    		else return (get(H, A, C) + get(A, H, C)) % MOD;
    	}
    };
    

    topcoder - 2017TCOAlgorithmFinal D1L2:忽略空位,结论是对于 ABA....BA 这种交替出现(要求极长,即不能再往外延伸)的段,只有最后一个字符对应的玩家才会移动这个段中的棋子。因此直接从后往前 dp,记录两个玩家能够移动的步数之差。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    class GameOfTokens{
    	private :
    	#define MOD 1000000007
    	#define MAXN 50
    	#define MAXM 2500
    	bool flag;
    	int f[2][MAXN + 5][2*MAXM + 5];
    	int t[2][MAXN + 5][2*MAXM + 5];
    	int n, m;
    	void get_temp() {
    		for(int i=0;i<=n;i++)
    			for(int j=0;j<=2*m;j++) {
    				t[0][i][j] = f[0][i][j], f[0][i][j] = 0;
    				t[1][i][j] = f[1][i][j], f[1][i][j] = 0;
    			}
    	}
    	void trans(int op) {
    		if( op == -1 ) {
    			for(int i=0;i<=n;i++)
    				for(int j=0;j<=2*m;j++) {
    					if( j + i <= 2*m )
    						f[0][i][j+i] = (f[0][i][j+i] + t[0][i][j]) % MOD;
    					if( j - i >= 0 )
    						f[1][i][j-i] = (f[1][i][j-i] + t[1][i][j]) % MOD;
    				}
    		}
    		else {
    			if( flag ) {
    				f[op][1][m] = (f[op][1][m] + 1) % MOD;
    			}
    			for(int i=0;i<=n;i++)
    				for(int j=0;j<=2*m;j++) {
    					if( i + 1 <= n )
    						f[op][i+1][j] = (f[op][i+1][j] + t[op][i][j]) % MOD;
    					if( i )
    						f[op][0][j] = (f[op][0][j] + t[!op][i][j]) % MOD;
    				}
    			for(int j=0;j<=2*m;j++) {
    				f[op][1][j] = (f[op][1][j] + t[!op][0][j]) % MOD;
    			}
    		}
    	}
    	public :
    	int count(string pattern) {
    		n = pattern.size(), m = n*n;
    		flag = true;
    		for(int i=n-1;i>=0;i--) {
    			get_temp();
    			if( pattern[i] == '?' )
    				trans(0), trans(1), trans(-1);
    			else if( pattern[i] == '.' )
    				trans(-1);
    			else trans(pattern[i] - 'A'), flag = false;
    		}
    		int ans = 0;
    		for(int i=m+1;i<=2*m;i++)
    			for(int j=0;j<=n;j++)
    				ans = (ans + (f[0][j][i] + f[1][j][i]) % MOD) % MOD;
    		return ans;
    	}
    };
    

    codeforces - 1280D:定义 dp[i][j] 表示以 i 为根已经分割出了 j 个连通块,此时能够获胜的连通块个数与 i 所在连通块的票数差。贪心地想,不能够牺牲能够获胜的连通块个数提高 i 所在连通块的票数差(后者最多贡献 1)。所以以获胜连通块个数为第一关键字,票数差为第二关键字树上背包 dp。经典时间复杂度 O(nm)。

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    typedef pair<int, ll> pil;
    
    const int MAXN = 3000;
    
    struct edge{
    	int to; edge *nxt;
    }edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt;
    void addedge(int u, int v) {
    	edge *p = (++ecnt);
    	p->to = v, p->nxt = adj[u], adj[u] = p;
    	p = (++ecnt);
    	p->to = u, p->nxt = adj[v], adj[v] = p;
    }
    void clear(int n) {
    	for(int i=1;i<=n;i++)
    		adj[i] = NULL;
    	ecnt = edges;
    }
    
    int siz[MAXN + 5], n, m;
    ll a[MAXN + 5], b[MAXN + 5];
    pil dp[MAXN + 5][MAXN + 5], t[MAXN + 5];
    
    void dfs(int x, int f) {
    	siz[x] = 1, dp[x][1] = make_pair(0, b[x] - a[x]);
    	for(edge *p=adj[x];p;p=p->nxt) {
    		if( p->to == f ) continue;
    		dfs(p->to, x);
    		int t1 = min(siz[x], m), t2 = min(siz[p->to], m), t3 = min(siz[x] + siz[p->to], m);
    		for(int i=t1;i>=1;i--) t[i] = dp[x][i];
    		for(int i=t3;i>=1;i--) dp[x][i] = make_pair(-1, -1);
    		for(int i=t1;i>=1;i--)
    			for(int j=t2;j>=1;j--) {
    				if( i + j <= t3 ) {
    					int d = (dp[p->to][j].second > 0);
    					dp[x][i+j] = max(dp[x][i+j], make_pair(t[i].first + dp[p->to][j].first + d, t[i].second));
    				}
    				if( i + j - 1 <= t3 ) {
    					dp[x][i+j-1] = max(dp[x][i+j-1], make_pair(t[i].first + dp[p->to][j].first, t[i].second + dp[p->to][j].second));
    				}
    			}
    		siz[x] += siz[p->to];
    	}
    }
    
    void solve() {
    	scanf("%d%d", &n, &m), clear(n);
    	for(int i=1;i<=n;i++) scanf("%lld", &a[i]);
    	for(int i=1;i<=n;i++) scanf("%lld", &b[i]);
    	for(int i=1;i<n;i++) {
    		int u, v; scanf("%d%d", &u, &v);
    		addedge(u, v);
    	}
    	dfs(1, 0);
    	printf("%d
    ", dp[1][m].first + (dp[1][m].second > 0));
    }
    
    int main() {
    	int t; scanf("%d", &t);
    	while( t-- ) solve();
    }
    

    codeforces - 1276D:考虑成如果边 e 选择了 x,则删除 x 相邻的所有边。定义 dp[0][x] - (x, fa[x]) 在 x 处被删除,或选择了 x;dp[1][x] - (x, fa[x]) 不能在 x 处被删除,也不能选择 x;dp[2][x] - (x, fa[x]) 将会在 fa[x] 处被删除,不会造成影响。转移时要么有一条边选中 x,要么所有边都不选中 x。时间复杂度 O(n)。

    #include <cstdio>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int MAXN = 200000;
    const int MOD = 998244353;
    
    typedef pair<int, int> pii;
    #define fi first
    #define se second
    #define mp make_pair
    
    vector<pii>G[MAXN + 5];
    void addedge(int u, int v, int w) {
    	G[u].push_back(mp(w, v));
    	G[v].push_back(mp(w, u));
    }
    
    int dp[3][MAXN + 5], pro[MAXN + 5];
    void dfs(int x, int f) {
    	for(int i=0;i<(int)G[x].size();i++)
    		if( G[x][i].se != f )
    			dfs(G[x][i].se, x);
    	int lst = 1, tmp = 0;
    	for(int i=0;i<(int)G[x].size();i++) {
    		if( G[x][i].se == f ) {
    			tmp = lst;
    			continue;
    		}
    		pro[G[x][i].se] = lst, lst = 1LL*lst*dp[0][G[x][i].se]%MOD;
    	}
    	dp[1][x] = dp[2][x] = lst, lst = 1;
    	bool flag = false;
    	for(int i=(int)G[x].size()-1;i>=0;i--) {
    		if( G[x][i].se == f ) {
    			dp[0][x] = (dp[0][x] + 1LL*lst*tmp%MOD)%MOD;
    			flag = true;
    		}
    		else {
    			if( flag ) {
    				dp[0][x] = (dp[0][x] + 1LL*lst*pro[G[x][i].se]%MOD*dp[1][G[x][i].se]%MOD)%MOD;
    			}
    			else {
    				dp[1][x] = (dp[1][x] + 1LL*lst*pro[G[x][i].se]%MOD*dp[1][G[x][i].se]%MOD)%MOD;
    			}
    			dp[2][x] = (dp[2][x] + 1LL*lst*pro[G[x][i].se]%MOD*dp[1][G[x][i].se]%MOD)%MOD;
    			lst = 1LL*lst*dp[2][G[x][i].se]%MOD;
    		}
    	}
    }
    /*
    dp[0][x] - (x, fa[x]) 在 x 处被删除,或选择了 x 
    dp[1][x] - (x, fa[x]) 不能在 x 处被删除,也不能选择 x
    dp[2][x] - (x, fa[x]) 将会在 fa[x] 处被删除,不会造成影响
    */
    int main() {
    	int n; scanf("%d", &n);
    	for(int i=1;i<n;i++) {
    		int u, v; scanf("%d%d", &u, &v);
    		addedge(u, v, i);
    	}
    	dfs(1, 0), printf("%d
    ", dp[1][1]);
    }
    

    bzoj - 3924:显然答案在带权重心,求出带权重心后直接动态点分治可以求它要的东西。注意到每个点的度数 <= 20,所以可以直接在点分树上暴力往下找带权重心。你可以像我一样判断两个点离其他点的距离和(这个再用点分治查嘛)然后 O(nlog^3n) 靠信仰 AC(跑不满)

    #include <cstdio>
    #include <vector>
    using namespace std;
    
    typedef long long ll;
    
    const int MAXN = 100000;
    
    struct edge{
    	int to, dis;
    	edge *nxt;
    }edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt = edges;
    void addedge(int u, int v, int w) {
    	edge *p = (++ecnt);
    	p->to = v, p->dis = w, p->nxt = adj[u], adj[u] = p;
    	p = (++ecnt);
    	p->to = u, p->dis = w, p->nxt = adj[v], adj[v] = p;
    }
    
    bool vis[MAXN + 5]; int siz[MAXN + 5], hvy[MAXN + 5];
    int get_size(int x, int f) {
    	siz[x] = 1;
    	for(edge *p=adj[x];p;p=p->nxt) {
    		if( vis[p->to] || p->to == f ) continue;
    		siz[x] += get_size(p->to, x);
    	}
    	return siz[x];
    }
    int get_G(int x, int f, int tot) {
    	hvy[x] = tot - siz[x];
    	int ret = -1;
    	for(edge *p=adj[x];p;p=p->nxt) {
    		if( vis[p->to] || p->to == f ) continue;
    		int tmp = get_G(p->to, x, tot);
    		if( ret == -1 || hvy[ret] > hvy[tmp] )
    			ret = tmp;
    		hvy[x] = max(hvy[x], siz[p->to]);
    	}
    	if( ret == -1 || hvy[ret] > hvy[x] )
    		ret = x;
    	return ret;
    }
    ll dis[20][MAXN + 5];
    void get_dis(int x, int f, ll d, int dep) {
    	dis[dep][x] = d;
    	for(edge *p=adj[x];p;p=p->nxt) {
    		if( vis[p->to] || p->to == f ) continue;
    		get_dis(p->to, x, d + p->dis, dep);
    	}
    }
    int fa[MAXN + 5], dep[MAXN + 5];
    vector<int>ch[MAXN + 5], ch2[MAXN + 5];
    void divide(int x, int d) {
    	dep[x] = d, vis[x] = true;
    	get_dis(x, 0, 0, d);
    	for(edge *p=adj[x];p;p=p->nxt) {
    		if( vis[p->to] ) continue;
    		int G = get_G(p->to, 0, get_size(p->to, 0));
    		fa[G] = x, ch[x].push_back(G), ch2[x].push_back(p->to);
    		divide(G, d + 1);
    	}
    }
    ll sum[MAXN + 5], cnt[MAXN + 5];
    ll s2[MAXN + 5], c2[MAXN + 5];
    void modify(int x, ll e) {
    	int d = dep[x];
    	sum[x] += dis[d][x]*e, cnt[x] += e;
    	for(int i=x;fa[i];i=fa[i]) {
    		int d = dep[fa[i]];
    		s2[i] += dis[d][x]*e, sum[fa[i]] += dis[d][x]*e;
    		c2[i] += e, cnt[fa[i]] += e;
    	}
    }
    ll query(int x) {
    	ll ans = sum[x];
    	for(int i=x;fa[i];i=fa[i]) {
    		int d = dep[fa[i]];
    		ans += dis[d][x]*(cnt[fa[i]] - c2[i]) + (sum[fa[i]] - s2[i]);
    	}
    	return ans;
    }
    int G;
    ll get() {
    	int x = G;
    	while( true ) {
    		int to = -1; ll t = query(x);
    		for(int i=0;i<ch[x].size();i++)
    			if( query(ch2[x][i]) < t )
    				to = ch[x][i];
    		if( to == -1 )
    			break;
    		else x = to;
    	}
    	return query(x);
    }
    int main() {
    	int n, Q; scanf("%d%d", &n, &Q);
    	for(int i=1;i<n;i++) {
    		int a, b, c; scanf("%d%d%d", &a, &b, &c);
    		addedge(a, b, c);
    	}
    	G = get_G(1, 0, get_size(1, 0)), divide(G, 0);
    	for(int i=1;i<=Q;i++) {
    		int u, e; scanf("%d%d", &u, &e);
    		modify(u, e), printf("%lld
    ", get());
    	}
    }
    

    codechef - Jump mission:如果不看 Pi < Pj 只看 i < j 就是个裸的李超线段树维护斜率优化 dp。如果加上 Pi < Pj 且 i < j 外层套一个类二维偏序的分治即可。时间复杂度 O(nlog^2n) 有信仰就能过。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    
    const int MAXH = 6*int(1E5);
    const int MAXN = 300000;
    const ll INF = (1LL << 60);
    
    struct line{
    	ll k, b;
    	line(ll _k=0, ll _b=INF) : k(_k), b(_b) {}
    	ll get(ll x) {return k * x + b;}
    };
    
    struct segtree{
    	int ch[2][2*MAXH + 5], root, ncnt;
    	line lne[2*MAXH + 5];
    	int newnode() {
    		ncnt++;
    		ch[0][ncnt] = ch[1][ncnt] = 0;
    		lne[ncnt] = line(0, INF);
    		return ncnt;
    	}
    	void insert(int &x, int l, int r, line ln) {
    		if( !x ) x = newnode();
    		if( lne[x].get(l) > ln.get(l) )
    			swap(ln, lne[x]);
    		int m = (l + r) >> 1;
    		if( lne[x].get(m) > ln.get(m) )
    			swap(ln, lne[x]), insert(ch[0][x], l, m, ln);
    		else if( lne[x].get(r) > ln.get(r) )
    			insert(ch[1][x], m + 1, r, ln);
    	}
    	ll query(int x, int l, int r, int p) {
    		if( l == r ) return lne[x].get(p);
    		int m = (l + r) >> 1;
    		if( p <= m ) return min(query(ch[0][x], l, m, p), lne[x].get(p));
    		else return min(query(ch[1][x], m + 1, r, p), lne[x].get(p));
    	}
    	void clear() {ncnt = root = 0;}
    }T;
    struct node{
    	ll dp;
    	int P, Q, A, H;
    }a[MAXN + 5], t[MAXN + 5];
    bool cmpP(node a, node b) {return a.P < b.P;}
    bool cmpQ(node a, node b) {return a.Q < b.Q;}
    void solve(int l, int r) {
    	if( l == r ) {
    		a[l].dp += a[l].A + 1LL*a[l].H*a[l].H;
    		return ;
    	}
    	int m = (l + r) >> 1;
    	solve(l, m);
    	sort(a + m + 1, a + r + 1, cmpP);
    	int pl = l, pr = m + 1, pt = l;
    	while( pl <= m && pr <= r ) {
    		if( a[pl].P < a[pr].P )
    			T.insert(T.root, 1, MAXH, line(-2*a[pl].H, 1LL*a[pl].H*a[pl].H + a[pl].dp)), pl++;
    		else a[pr].dp = min(a[pr].dp, T.query(T.root, 1, MAXH, a[pr].H)), pr++;
    	}
    	while( pl <= m ) T.insert(T.root, 1, MAXH, line(-2*a[pl].H, 1LL*a[pl].H*a[pl].H + a[pl].dp)), pl++;
    	while( pr <= r ) a[pr].dp = min(a[pr].dp, T.query(T.root, 1, MAXH, a[pr].H)), pr++;
    	T.clear();
    	sort(a + m + 1, a + r + 1, cmpQ);
    	solve(m + 1, r);
    	pl = l, pr = m + 1, pt = l;
    	while( pl <= m && pr <= r ) {
    		if( a[pl].P < a[pr].P )
    			t[pt++] = a[pl++];
    		else t[pt++] = a[pr++];
    	}
    	while( pl <= m ) t[pt++] = a[pl++];
    	while( pr <= r ) t[pt++] = a[pr++];
    	for(int i=l;i<=r;i++) a[i] = t[i];
    }
    int main() {
    	int N; scanf("%d", &N);
    	for(int i=1;i<=N;i++) scanf("%d", &a[i].P);
    	for(int i=1;i<=N;i++) a[i].Q = i;
    	for(int i=1;i<=N;i++) scanf("%d", &a[i].A);
    	for(int i=1;i<=N;i++) scanf("%d", &a[i].H);
    	for(int i=2;i<=N;i++) a[i].dp = INF;
    	a[1].dp = -1LL * a[1].H * a[1].H;
    	solve(1, N);
    /*
    	for(int i=1;i<=N;i++)
    		printf("%d %d %lld
    ", a[i].Q, a[i].P, a[i].dp);
    */
    	printf("%lld
    ", a[N].dp);
    }
    

    codeforces - 438E:记 (F(x)) 表示权值为 i 的二叉树个数的生成函数,记 (G(x) = sum_{iin S}x^i),有 (F(x) = F^2(x) imes G(x) + 1),解二次方程即可。注意通过常数项舍掉一个解,以及分母分子需要消掉公因子才能作多项式逆元。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int MOD = 998244353;
    const int MAXN = 800000;
    
    namespace poly{
    	int w[20], iw[20], inv[MAXN + 5];
    	int pow_mod(int b, int p) {
    		int ret = 1;
    		while( p ) {
    			if( p & 1 ) ret = 1LL*ret*b%MOD;
    			b = 1LL*b*b%MOD;
    			p >>= 1;
    		}
    		return ret;
    	}
    	void init() {
    		inv[1] = 1;
    		for(int i=2;i<=MAXN;i++)
    			inv[i] = (MOD - 1LL*inv[MOD%i]*(MOD/i)%MOD)%MOD;
    		for(int i=0;i<20;i++) {
    			w[i] = pow_mod(3, (MOD - 1)/(1 << i));
    			iw[i] = pow_mod(w[i], MOD - 2);
    		}
    	}
    	void ntt(int *A, int n, int type) {
    		for(int i=0,j=0;i<n;i++) {
    			if( i < j ) swap(A[i], A[j]);
    			for(int k=(n>>1);(j^=k)<k;k>>=1);
    		}
    		for(int i=1;(1<<i)<=n;i++) {
    			int s = (1 << i), t = (s >> 1);
    			int u = (type == 1 ? w[i] : iw[i]);
    			for(int j=0;j<n;j+=s) {
    				for(int k=0,p=1;k<t;k++,p=1LL*p*u%MOD) {
    					int x = A[j+k], y = A[j+k+t];
    					A[j+k] = (x + 1LL*y*p%MOD)%MOD;
    					A[j+k+t] = (x + MOD - 1LL*y*p%MOD)%MOD;
    				}
    			}
    		}
    		if( type == -1 ) {
    			int iv = inv[n];
    			for(int i=0;i<n;i++)
    				A[i] = 1LL*A[i]*iv%MOD;
    		}
    	}
    	int length(int n) {
    		int len; for(len = 1; len < n; len <<= 1);
    		return len;
    	}
    	int t1[MAXN + 5], t2[MAXN + 5];
    	void copy(int *A, int *B, int nB, int len) {
    		for(int i=0;i<nB;i++) A[i] = B[i];
    		for(int i=nB;i<len;i++) A[i] = 0;
    	}
    	void pmul(int *A, int nA, int *B, int nB, int *C) {
    		int len = length(nA + nB - 1);
    		copy(t1, A, nA, len);
    		copy(t2, B, nB, len);
    		ntt(t1, len, 1), ntt(t2, len, 1);
    		for(int i=0;i<len;i++)
    			C[i] = 1LL*t1[i]*t2[i]%MOD;
    		ntt(C, len, -1);
    	}
    	int t3[MAXN + 5], t4[MAXN + 5];
    	void pinv(int *A, int *B, int n) {
    		if( n == 1 ) {
    			B[0] = pow_mod(A[0], MOD-2);
    			return ;
    		}
    		int m = (n + 1) >> 1;
    		pinv(A, B, m);
    		int len = length(n << 1);
    		copy(t3, A, n, len);
    		copy(t4, B, m, len);
    		ntt(t3, len, 1), ntt(t4, len, 1);
    		for(int i=0;i<len;i++)
    			B[i] = 1LL*t4[i]*(2 + MOD - 1LL*t3[i]*t4[i]%MOD)%MOD;
    		ntt(B, len, -1);
    	}
    	int t5[MAXN + 5], t6[MAXN + 5];
    	void psqrt(int *A, int *B, int n) {
    		if( n == 1 ) {
    			B[0] = MOD - 1;
    			return ;
    		}
    		int m = (n + 1) >> 1;
    		psqrt(A, B, m);
    		int len = length(n << 1);
    		copy(t5, B, m, len);
    		pinv(t5, t6, n);
    		copy(t5, A, n, len);
    		pmul(t5, n, t6, n, t6);
    		copy(t5, B, m, n);
    		for(int i=0;i<n;i++)
    			B[i] = 1LL*inv[2]*(t5[i] + t6[i])%MOD;
    	}
    }
    
    int f[MAXN + 5], g[MAXN + 5], t1[MAXN + 5], t2[MAXN + 5], t3[MAXN + 5];
    int main() {
    	int n, m, c, mn = MAXN; scanf("%d%d", &n, &m);
    	for(int i=1;i<=n;i++)
    		scanf("%d", &c), mn = min(mn, c), g[c]++;
    	m = m + mn + 1;
    	poly::init();
    	for(int i=1;i<m;i++)
    		t3[i] = (MOD - 4LL*g[i]%MOD) % MOD;
    	t3[0] = 1;
    	poly::psqrt(t3, t2, m);
    	for(int i=mn;i<m;i++)
    		t2[i-mn] = t2[i];
    	for(int i=mn;i<m;i++)
    		t3[i-mn] = 2LL*g[i]%MOD;
    	poly::pinv(t3, t1, m);
    	poly::pmul(t1, m, t2, m, f);
    	for(int i=1;i<m-mn;i++)
    		printf("%d
    ", f[i]);
    }
    

    atcoder - ARC062F:一眼 polya。点双分解,分三种情况贡献。边数<点数(一条边);边数=点数(一个简单环);边数>点数的点双。前两个的循环数量很好求,最后一个可以证明所有置换都是可行置换(手玩一下就证出来了)。然后把所有点双作背包。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int MAXN = 50;
    const int MAXM = 100;
    const int MOD = int(1E9) + 7;
    
    int pow_mod(int b, int p) {
    	int ret = 1;
    	while( p ) {
    		if( p & 1 ) ret = 1LL*ret*b%MOD;
    		b = 1LL*b*b%MOD;
    		p >>= 1;
    	}
    	return ret;
    }
    
    int N, M, K;
    
    int s[MAXM + 5][MAXM + 5], fct[MAXM + 5];
    void init() {
    	s[0][0] = 1;
    	for(int i=1;i<=MAXM;i++)
    		for(int j=1;j<=i;j++)
    			s[i][j] = (s[i-1][j-1] + 1LL*(i-1)*s[i-1][j]%MOD) % MOD;
    	fct[0] = 1;
    	for(int i=1;i<=MAXM;i++)
    		fct[i] = 1LL*fct[i-1]*i%MOD;
    }
    
    struct edge{
    	int to; edge *nxt;
    }edges[2*MAXM + 5], *adj[MAXM + 5], *ecnt = edges;
    void addedge(int u, int v) {
    	edge *p = (++ecnt);
    	p->to = v, p->nxt = adj[u], adj[u] = p;
    	p = (++ecnt);
    	p->to = u, p->nxt = adj[v], adj[v] = p;
    }
    
    int f[2][MAXM + 5];
    void copy() {
    	for(int i=0;i<=M;i++)
    		f[1][i] = f[0][i], f[0][i] = 0;
    }
    void update(int x, int k) {
    	for(int i=x;i<=M;i++)
    		f[0][i] = (f[0][i] + 1LL*f[1][i-x]*k%MOD)%MOD;
    }
    
    int gcd(int x, int y) {
    	return y == 0 ? x : gcd(y, x % y);
    }
    
    int dfn[MAXM + 5], low[MAXM + 5], dcnt;
    int stk[MAXM + 5], Gsiz, tp;
    void dfs(int x, int fa) {
    	dfn[x] = low[x] = (++dcnt);
    	for(edge *p=adj[x];p;p=p->nxt) {
    		if( p->to == fa ) continue;
    		if( !dfn[p->to] ) {
    			stk[++tp] = p->to, dfs(p->to, x);
    			if( low[p->to] >= dfn[x] ) {
    				int cnt = 0, tot = 0;
    				do {
    					if( stk[tp] == -1 ) cnt++;
    					tot++;
    				}while( stk[tp--] != p->to );
    				copy();
    				if( cnt == 0 ) update(1, 1);
    				else if( cnt == 1 ) {
    					for(int i=1;i<=tot;i++)
    						update(gcd(tot, i), 1);
    					Gsiz = 1LL*Gsiz*tot%MOD;
    				}
    				else {
    					for(int i=1;i<=tot;i++)
    						update(i, s[tot][i]);
    					Gsiz = 1LL*Gsiz*fct[tot]%MOD;
    				}
    			}
    			low[x] = min(low[x], low[p->to]);
    		}
    		else {
    			low[x] = min(low[x], dfn[p->to]);
    			if( dfn[p->to] < dfn[x] )
    				stk[++tp] = -1;
    		}
    	}
    }
    int main() {
    	init(), scanf("%d%d%d", &N, &M, &K);
    	for(int i=1;i<=M;i++) {
    		int u, v; scanf("%d%d", &u, &v);
    		addedge(u, v);
    	}
    	Gsiz = f[0][0] = 1;
    	for(int i=1;i<=N;i++)
    		if( !dfn[i] ) dfs(i, 0);
    	int ans = 0;
    	for(int i=0,t=1;i<=M;i++,t=1LL*t*K%MOD)
    		ans = (ans + 1LL*t*f[0][i]%MOD) % MOD;
    	printf("%lld
    ", 1LL*ans*pow_mod(Gsiz, MOD-2)%MOD);
    }
    

    uoj - 422:一眼 min-max 容斥。发现 n 很小所以对 n 那一维状压,状态里面再存一个“有多少相邻的格子里面至少有一个喜欢的”。时间复杂度 O(4^n*m*n*m) 但是跑不满。理论上可以把 O(4^n) 优化成 O(3^n)。

    #include <cstdio>
    
    const int MOD = 998244353;
    void add(int &x, int y) {
    	x = (x + y) % MOD;
    }
    
    int inv[1200], g[1<<6], h[1<<6], b[1<<6];
    
    char str[6][100];
    int n, m, t, a[100];
    void init() {
    	inv[1] = 1;
    	for(int i=2;i<=t;i++)
    		inv[i] = MOD - 1LL*(MOD/i)*inv[MOD%i]%MOD;
    	g[0] = 1;
    	for(int i=1;i<(1<<n);i++) {
    		if( i & 1 ) g[i] = (g[i >> 1] == 1 ? MOD - 1 : 1);
    		else g[i] = g[i >> 1];
    		b[i] = b[i >> 1] + (i & 1);
    	}
    	for(int i=0;i<(1<<n);i++) {
    		for(int j=1;j<n;j++)
    			h[i] += ((i & (1 << (j - 1))) || (i & (1 << j)));
    	}
    }
    
    
    int f[2][1<<6][1200];
    int main() {
    	scanf("%d%d", &n, &m), t = (n-1)*m + n*(m-1), init();
    	for(int i=0;i<n;i++)
    		scanf("%s", str[i]);
    	for(int j=0;j<m;j++)
    		for(int i=0;i<n;i++)
    			if( str[i][j] == '*' )
    				a[j] |= (1 << i);
    	int s1, s2; s1 = s2 = a[0];
    	do {
    		add(f[0][s2][h[s2]], g[s2]);
    		
    		if( s2 == 0 ) break;
    		s2 = s1 & (s2 - 1);
    	}while( true );
    	for(int j=1;j<m;j++) {
    		int lim = (j - 1)*n + j*(n - 1);
    		
    		int s3 = a[j-1], s4 = a[j-1];
    		do {
    			for(int i=0;i<=lim;i++)
    				f[1][s4][i] = f[0][s4][i], f[0][s4][i] = 0;
    			
    			if( s4 == 0 ) break;
    			s4 = s3 & (s4 - 1);
    		}while( true );
    		
    		s1 = a[j], s3 = s4 = a[j-1];
    		do {
    			s2 = a[j];
    			do {
    				int p = b[s2 | s4] + h[s2];
    				for(int i=0;i<=lim;i++)
    					add(f[0][s2][i + p], 1LL*f[1][s4][i]*g[s2]%MOD);
    				
    				if( s2 == 0 ) break;
    				s2 = s1 & (s2 - 1);
    			}while( true );
    			
    			if( s4 == 0 ) break;
    			s4 = s3 & (s4 - 1);
    		}while( true );
    	}
    	int ans = 0; s1 = s2 = a[m-1];
    	do {
    		for(int j=1;j<=t;j++)
    			ans = (ans + 1LL*f[0][s2][j]*inv[j]%MOD) % MOD;
    		if( s2 == 0 ) break;
    		s2 = s1 & (s2 - 1);
    	}while( true );
    	printf("%lld
    ", 1LL*ans*(MOD-1)%MOD*t%MOD);
    }
    

    atcoder - ARC093F:1 号人会打 N 场比赛,第 i 场比赛对手是某 2^i 个人中的最小值。容斥,枚举哪些 Ai 是最小值,从大到小枚举 Ai 考虑贡献。可用状压 dp 代替容斥。最后不要忘记乘个系数得到排列总数。

    #include <cstdio>
    
    const int MAXN = (1<<16);
    const int MOD = int(1E9) + 7;
    
    int pow_mod(int b, int p) {
    	int ret = 1;
    	while( p ) {
    		if( p & 1 ) ret = 1LL*ret*b%MOD;
    		b = 1LL*b*b%MOD;
    		p >>= 1;
    	}
    	return ret;
    }
    
    int fct[MAXN + 5], ifct[MAXN + 5];
    void init() {
    	fct[0] = 1;
    	for(int i=1;i<=MAXN;i++)
    		fct[i] = 1LL*fct[i-1]*i%MOD;
    	ifct[MAXN] = pow_mod(fct[MAXN], MOD - 2);
    	for(int i=MAXN-1;i>=0;i--)
    		ifct[i] = 1LL*ifct[i+1]*(i+1)%MOD;
    }
    int comb(int n, int m) {
    	return 1LL*fct[n]*ifct[m]%MOD*ifct[n-m]%MOD;
    }
    
    int f[MAXN + 5], dp[MAXN + 5];
    int lowbit(int x) {
    	return x & -x;
    }
    
    int A[16], N, M, t;
    int main() {
    	init();
    	scanf("%d%d", &N, &M), t = (1 << N);
    	for(int i=0;i<M;i++)
    		scanf("%d", &A[i]);
    	dp[0] = f[0] = 1;
    	for(int i=1;i<t;i++)
    		f[i] = 1LL*comb(i, lowbit(i))*f[i^lowbit(i)]%MOD;
    	for(int i=M-1;i>=0;i--) {
    		for(int s=t-1;s>=0;s--) {
    			int p = (t - A[i]) - s;
    			for(int j=0;j<N;j++) {
    				int q = (1 << j);
    				if( !(s & q) && p >= q - 1 )
    					dp[s|q] = (dp[s|q] + MOD - 1LL*dp[s]*comb(p,q-1)%MOD) % MOD;
    			}
    		}
    	}
    	int ans = 0;
    	for(int i=0;i<t;i++) ans = (ans + 1LL*dp[i]*f[t-1-i]%MOD) % MOD;
    	for(int i=0;i<N;i++) ans = 1LL*ans*fct[1 << i] % MOD;
    	printf("%lld
    ", 1LL*ans*t%MOD);
    }
    

    bzoj - 4001(我真的只是为了练习如何推生成函数)不难写出结点数为 n 时树的方案数的生成函数 (F(x) = F^2(x)x + 1) (就是卡特兰数)与叶子总数生成函数 (G(x) = 2G(x)F(x)x + x)。解方程然后泰勒展开,注意 (1 imes 3 imes dots imes (2r-1) imes 2^r = frac{(2r)!}{r!})。答案为 (g_n/f_n)

    #include <cstdio>
    
    int main() {
    	int n; scanf("%d", &n);
    	printf("%.9f
    ", 1.0*n/(2*n - 1)*(n + 1)/2);
    }
    

    bzoj - 5306:枚举恰好为 S 的有多少个,写出生成函数式子:

    [N! imes[x^N]sum_{i=0}^{M}(w_i imes (frac{x^S}{S!})^i imes C_{M}^{i} imes (e^x - frac{x^S}{S!})^{M-i}) ]

    然后令 (p = frac{x^S}{S!}, q = e^x),拆开二项式,化简式子,然后卷积,然后没了。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int MOD = 1004535809;
    const int MAXN = 10000000;
    const int MAXM = 4*100000;
    const int G = 3;
    
    inline int mul(int a, int b) {return 1LL*a*b % MOD;}
    inline int add(int a, int b) {return (a + b >= MOD ? a + b - MOD : a + b);}
    inline int sub(int a, int b) {return (a - b < 0 ? a - b + MOD : a - b);}
    
    int pow_mod(int b, int p) {
    	int ret = 1;
    	for(int i=p;i;i>>=1,b=mul(b,b))
    		if( i & 1 ) ret = mul(ret,b);
    	return ret;
    }
    
    int N, M, S, tmp = 1;
    
    int w[20], iw[20], ifct[MAXN + 5];
    void init() {
    	for(int i=0;i<20;i++) {
    		w[i] = pow_mod(G, (MOD - 1)/(1 << i));
    		iw[i] = pow_mod(w[i], MOD - 2);
    	}
    	int fct = 1, lim = max(M - 1, max(N, S));
    	for(register int i=1;i<=lim;i++) {
    		fct = mul(fct, i);
    		if( i == M - 1 ) tmp = mul(tmp, fct);
    		if( i == N ) tmp = mul(tmp, fct);
    	}
    	ifct[lim] = pow_mod(fct, MOD - 2);
    	for(register int i=lim-1;i>=0;i--) ifct[i] = mul(ifct[i+1], i+1);
    }
    
    int length(int n) {
    	int len; for(len = 1; len < n; len <<= 1);
    	return len;
    }
    void ntt(int *A, int n, int type) {
    	for(int i=0,j=0;i<n;i++) {
    		if( i < j ) swap(A[i], A[j]);
    		for(int k=(n>>1);(j^=k)<k;k>>=1);
    	}
    	for(int i=1;(1<<i)<=n;i++) {
    		int s = (1 << i), t = (s >> 1);
    		int u = (type == 1 ? w[i] : iw[i]);
    		for(int j=0;j<n;j+=s) {
    			for(int k=0,p=1;k<t;k++,p=mul(p,u)) {
    				int x = A[j+k], y = mul(A[j+k+t], p);
    				A[j+k] = add(x, y), A[j+k+t] = sub(x, y);
    			}
    		}
    	}
    	if( type == -1 ) {
    		int iv = pow_mod(n, MOD - 2);
    		for(int i=0;i<n;i++)
    			A[i] = mul(A[i], iv);
    	}
    }
    
    int A[MAXM + 5], f[MAXM + 5], g[MAXM + 5];
    void get() {
    	for(int i=0;i<M;i++) {
    		f[i] = mul(A[i], ifct[i]);
    		g[i] = ((i & 1) ? MOD - ifct[i]: ifct[i]);
    	}
    	int len = length(M << 1);
    	ntt(f, len, 1), ntt(g, len, 1);
    	for(int i=0;i<len;i++)
    		f[i] = mul(f[i], g[i]);
    	ntt(f, len, -1);
    }
    
    int read() {
    	int x = 0; char ch = getchar();
    	while( ch > '9' || ch < '0' ) ch = getchar();
    	while( '0' <= ch && ch <= '9' ) x = 10*x + ch - '0', ch = getchar();
    	return x;
    }
    
    int main() {
    	N = read(), M = read() + 1, S = read(), init();
    	for(int i=0;i<M;i++) A[i] = read();
    	get();
    	int ans = 0;
    	for(int i=0;i<M;i++) {
    		int j = M - i - 1;
    		if( j <= N/S ) {
    			int p = N - j*S;
    			int a = mul(f[j], ifct[i]), b = pow_mod(ifct[S], j);
    			int c = mul(pow_mod(i, p), ifct[p]);
    			ans = add(ans, mul(a, mul(b, c)));
    		}
    	}
    	printf("%d
    ", mul(tmp, ans));
    }
    

    Atcoder - ARC103F:注意到 Di 最大的一定是叶子,且往父亲走一步 D 的变化值为 (D - size[i]) + (size[i])。按 Di 从大到小递推,维护每个结点子树的 size,即可得到可能的树形态。注意还要 dfs 一遍判断根的 D 值是否合法。

    #include <map>
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    
    const int MAXN = 100000;
    
    vector<int>G[MAXN + 5];
    map<ll, int>mp;
    
    int siz[MAXN + 5];
    
    ll check(int x) {
    	ll ret = 0;
    	for(int i=0;i<G[x].size();i++) {
    		int to = G[x][i];
    		ret += check(to) + siz[to];
    	}
    	return ret;
    }
    
    int main() {
    	int n; scanf("%d", &n);
    	for(int i=1;i<=n;i++) {
    		ll D; scanf("%lld", &D);
    		mp[D] = i, siz[i] = 1;
    	}
    	map<ll, int>::reverse_iterator rt;
    	for(map<ll, int>::reverse_iterator it=mp.rbegin();it!=mp.rend();it++) {
    		if( siz[it->second] == n ) {
    			rt = it;
    			break;
    		}
    		int x = it->second;
    		ll k = it->first + siz[x] - (n - siz[x]);
    		if( k < it->first && mp.count(k) ) {
    			int p = mp[k];
    			siz[p] += siz[x];
    			G[p].push_back(x);
    		}
    		else {
    			puts("-1");
    			return 0;
    		}
    	}
    	if( check(rt->second) == rt->first ) {
    		for(int i=1;i<=n;i++) {
    			for(int j=0;j<G[i].size();j++)
    				printf("%d %d
    ", i, G[i][j]);
    		}
    	}
    	else puts("-1");
    }
    

    codechef - AMR16F:记 (P(x) = sum_{i=1}i^2x^i = frac{x^2 + x}{(1-x)^3}, Q(x) = sum_{i=1}6 imesfrac{i(i+1)}{2}x^i = frac{6x}{(1-x)^3}),则答案 (ans = [x^N]P^A(x)Q^B(x) = 6^Bfrac{x^{A+B}(x+1)^A}{(1-x)^{3A+3B}})。用广义二项式定理展开分子与分母,直接暴力乘求第 N 项(分母的系数需要递推着算)。

    #include <cstdio>
    
    const int MOD = 1234567891;
    const int MAXN = 600000;
    
    int pow_mod(int b, int p) {
    	int ret = 1;
    	for(int i=p;i;i>>=1,b=1LL*b*b%MOD)
    		if( i & 1 ) ret = 1LL*ret*b%MOD;
    	return ret;
    }
    
    int fct[MAXN + 5], ifct[MAXN + 5];
    void init() {
    	fct[0] = 1;
    	for(int i=1;i<=MAXN;i++)
    		fct[i] = 1LL*fct[i-1]*i%MOD;
    	ifct[MAXN] = pow_mod(fct[MAXN], MOD - 2);
    	for(int i=MAXN-1;i>=0;i--)
    		ifct[i] = 1LL*ifct[i+1]*(i+1)%MOD;
    }
    int comb(int n, int m) {
    	return 1LL*fct[n]*ifct[n-m]%MOD*ifct[m]%MOD;
    }
    
    void solve() {
    	int A, B, N; scanf("%d%d%d", &A, &B, &N);
    	if( A + B <= N ) {
    		N -= (A + B);
    		int M = 3*(A + B) - 1;
    		int del = 1LL*ifct[M]*pow_mod(6, B)%MOD;
    		int ans = 0, tmp = 1, p = M + N;
    		for(int i=p;i>p-M;i--) tmp = 1LL*tmp*i%MOD;
    		for(int j=0;j<=N&&j<=A;j++) {
    			ans = (ans + 1LL*comb(A, j)*tmp%MOD)%MOD;
    			tmp = 1LL*tmp*pow_mod(p,MOD-2)%MOD*(p-M)%MOD; p--;
    		}
    		printf("%d
    ", 1LL*ans*del%MOD);
    	}
    	else puts("0");
    }
    
    int main() {
    	init();
    	int T; scanf("%d", &T);
    	while( T-- ) solve();
    }
    

    codechef - CNTL:第一问随便写。第二问可以写出生成函数 (F(x) = sum_{i=0}frac{x^i}{(2i+1)!}, G(x) = sum_{i=0}frac{x^i}{(2i)!}),根据 N-K 的奇偶最终答案为 (N! imes[x^N]F^{K-1}(x)G(x))(N! imes[x^N]F^K(x))。注意到 (F(x) = sinh(x) = frac{e^x - e^{-x}}{2}, G(x) = cosh(x) = frac{e^x + e^{-x}}{2})(双曲函数),直接二项式展开即可。

    #include <cstdio>
    
    const int MOD = int(1E9) + 7;
    const int MAXN = 100000;
    
    int pow_mod(int b, int p) {
    	int ret = 1;
    	for(int i=p;i;i>>=1,b=1LL*b*b%MOD)
    		if( i & 1 ) ret = 1LL*ret*b%MOD;
    	return ret;
    }
    
    int fct[MAXN + 5], ifct[MAXN + 5];
    void init() {
    	fct[0] = 1;
    	for(int i=1;i<=MAXN;i++)
    		fct[i] = 1LL*fct[i-1]*i%MOD;
    	ifct[MAXN] = pow_mod(fct[MAXN], MOD-2);
    	for(int i=MAXN-1;i>=0;i--)
    		ifct[i] = 1LL*ifct[i+1]*(i+1)%MOD;
    }
    int comb(int n, int m) {
    	return 1LL*fct[n]*ifct[m]%MOD*ifct[n-m]%MOD;
    }
    
    void solve() {
    	int N, K; scanf("%d%d", &N, &K);
    	int ans = 0;
    	if( (N - K) & 1 ) {
    		for(int i=0;i<=K-1;i++) {
    			int j = K - 1 - i, k = (i + MOD - j)%MOD;
    			int del = ((j & 1) ? MOD - comb(K-1, i): comb(K-1, i)) % MOD;
    			ans = (ans + 1LL*del*pow_mod(k + 1, N))%MOD;
    			ans = (ans + 1LL*del*pow_mod((k + MOD - 1)%MOD, N))%MOD;
    		}
    	}
    	else {
    		for(int i=0;i<=K;i++) {
    			int j = K - i, k = (i + MOD - j)%MOD;
    			int del = ((j & 1) ? MOD - comb(K, i): comb(K, i)) % MOD;
    			ans = (ans + 1LL*del*pow_mod(k, N))%MOD;
    		}
    	}
    	printf("%d ", (pow_mod(2, K) + MOD - 1 - ((N - K)&1))%MOD);
    	printf("%d
    ", 1LL*ans*pow_mod(2, MOD-1-K)%MOD);
    }
    
    int main() {
    	init();
    	int T; scanf("%d", &T);
    	while( T-- ) solve();
    }
    
  • 相关阅读:
    抓取登录后的数据
    Form认证的几点说明
    eclipse启动错误:java.lang.NoClassDefFoundError: org/eclipse/core/resources/IContainer
    mysql游标的使用 No data
    mysql insert 主键 重复问题
    tail 命令
    maven 打包可执行jar的方法
    maven中如何打包源代码
    工程师,请优化你的代码
    在服务器端判断request来自Ajax请求(异步)还是传统请求(同步)
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12020918.html
Copyright © 2020-2023  润新知