zoukankan      html  css  js  c++  java
  • AtCoder Educational DP Contest

    A

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 100010;
    
    int n, k;
    int f[N], a[N];
    
    int main() {
    	scanf("%d", &n);
    	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    	for(int i = 2; i <= n; ++i) {
    		f[i] = 0x3f3f3f3f;
    		if(i >= 2) f[i] = min(f[i], f[i - 1] + abs(a[i] - a[i - 1]));
    		if(i >= 3) f[i] = min(f[i], f[i - 2] + abs(a[i] - a[i - 2]));
    	}
    	printf("%d
    ", f[n]);
    }
    

    B

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 100010;
    
    int n, k;
    int f[N], a[N];
    
    int main() {
    	scanf("%d%d", &n, &k);
    	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    	for(int i = 2; i <= n; ++i) {
    		f[i] = 0x3f3f3f3f;
    		for(int j = max(i - k, 1); j < i; ++j) {
    			f[i] = min(f[i], f[j] + abs(a[i] - a[j]));
    		}
    	}
    	printf("%d
    ", f[n]);
    }
    

    C

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 100010;
    
    int n, k;
    int f[N][3], a[N][3];
    
    int main() {
    	scanf("%d", &n);
    	for(int i = 1; i <= n; ++i) scanf("%d%d%d", &a[i][0], &a[i][1], &a[i][2]);
    	for(int i = 1; i <= n; ++i) {
    		for(int j = 0; j < 3; ++j) {
    			for(int k = 0; k < 3; ++k) {
    				if(j == k) continue;
    				f[i][j] = max(f[i][j], f[i - 1][k] + a[i][j]);
    			}
    		}
    	}
    	printf("%d
    ", max(max(f[n][0], f[n][1]), f[n][2]));
    }
    

    D

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    const int N = 100010;
    
    int n, W;
    ll f[N], w[N], v[N];
    
    int main() {
    	scanf("%d%d", &n, &W);
    	ll ans = 0;
    	for(int i = 1; i <= n; ++i) scanf("%lld%lld", &w[i], &v[i]);
    	for(int i = 1; i <= n; ++i) {
    		for(int j = W; j >= w[i]; --j) {
    			f[j] = max(f[j], f[j - w[i]] + v[i]);
    		}
    	}
    	for(int i = 1; i <= W; ++i) ans = max(ans, f[i]);
    	printf("%lld
    ", ans);
    }
    

    E

    对比前面四题不那么傻的一题
    很妙的一个转化,注意到这题的(W)很大,但是(v)很小,所以不妨转换一下状态,设(f[i,j])表示前(i)个物品,选出后的总价值为(j),需要的最小体积。那么有(f[i][j]=min{f[i-1][j-v[i]]+w[i]}),答案就是对于所有(f[n][j]le W)(j)(max)

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N = 100010;
    
    int n, v[N], W, w[N];
    int f[110][N];
    
    int main() {
    	scanf("%d%d", &n, &W);
    	int sum = 0;
    	for(int i = 1; i <= n; ++i) scanf("%d%d", &w[i], &v[i]), sum += v[i];
    	memset(f, 0x3f, sizeof(f));
    	f[0][0] = 0;
    	int ans = 0;
    	for(int i = 1; i <= n; ++i) {
    		for(int j = 0; j <= sum; ++j) {
    			if(j >= v[i]) f[i][j] = min(f[i][j], f[i - 1][j - v[i]] + w[i]);
    			f[i][j] = min(f[i][j], f[i - 1][j]);
    			if(f[i][j] <= W) ans = max(ans, j);
    		}
    	}
    	printf("%d
    ", ans);
    }
    

    F

    LCS做一下就好,输出方案的话从([n,m])往回找就好了,用个栈压一下答案。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 3010;
    
    char s[N], t[N], st[N];
    int f[N][N];
    
    int main() {
    	scanf("%s%s", s + 1, t + 1);
    	int n = strlen(s + 1), m = strlen(t + 1);
    	for(int i = 1; i <= n; ++i) {
    		for(int j = 1; j <= m; ++j) {
    			f[i][j] = max(f[i - 1][j], f[i][j - 1]);
    			if(s[i] == t[j]) f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
    		}
    	}
    	int now = f[n][m] + 1;
    	int top = 0;
    	for(int i = n; i; --i) {
    		for(int j = m; j; --j) {
    			if(s[i] == t[j] && f[i][j] == now - 1) {
    				--now;
    				st[++top] = s[i];
    				break;
    			}
    		}
    	}
    	while(top) putchar(st[top--]);
    }
    

    G

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 100010;
    
    int n, m, deg[N], q[N], f[N];
    int cnt, head[N];
    struct edge { int to, nxt; } e[N];
    
    void ins(int u, int v) {
    	e[++cnt] = (edge) { v, head[u] };
    	head[u] = cnt;
    }
    
    int main() {
    	scanf("%d%d", &n, &m);
    	for(int i = 1, u, v; i <= m; ++i) {
    		scanf("%d%d", &u, &v);
    		ins(u, v); deg[v]++;
    	}
    	int l = 1, r = 1;
    	for(int i = 1; i <= n; ++i) {
    		if(!deg[i]) q[r++] = i;
    	}
    	while(l < r) {
    		int u = q[l++];
    		for(int i = head[u]; i; i = e[i].nxt) {
    			int v = e[i].to;
    			f[v] = max(f[v], f[u] + 1);
    			deg[v]--;
    			if(!deg[v]) q[r++] = v;
    		}
    	}
    	int ans = 0;
    	for(int i = 1; i <= n ;++i) ans = max(ans, f[i]);
    	printf("%d
    ", ans);
    	return 0;
    }
    

    H

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 1010;
    const int mod = 1e9 + 7;
    
    int h, w, f[N][N];
    char a[N][N];
    
    int main() {
    	scanf("%d%d", &h, &w);
    	f[1][1] = 1;
    	for(int i = 1; i <= h; ++i) scanf("%s", a[i] + 1);
    	for(int i = 1; i <= h; ++i) {
    		for(int j = 1; j <= w; ++j) {
    			if(i == 1 && j == 1) continue;
    			if(a[i][j] == '.' && a[i - 1][j] == '.') 
    				(f[i][j] += f[i - 1][j]) %= mod;
    			if(a[i][j] == '.' && a[i][j - 1] == '.')
    				(f[i][j] += f[i][j - 1]) %= mod;
    		}
    	}
    	printf("%d
    ", f[h][w]);
    }
    

    I

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 3000;
    
    int n;
    double p[N], f[2][N];
    
    int main() {
    	scanf("%d", &n);
    	int cur = 0;
    	for(int i = 1; i <= n; ++i) scanf("%lf", &p[i]);
    	f[1][0] = 1;
    	for(int i = 1; i <= n; ++i) {
    		memset(f[cur], 0, sizeof(f[cur]));
    		for(int j = 0; j <= i; ++j) {
    			f[cur][j] += f[cur ^ 1][j] * (1 - p[i]);
    			f[cur][j] += f[cur ^ 1][j - 1] * p[i];
    		}
    		cur ^= 1;
    	}
    	double ans = 0;
    	for(int i = n / 2 + 1; i <= n; ++i) ans += f[cur ^ 1][i];
    	printf("%.15lf
    ", ans);
    }
    

    J

    (f[i][j][k])表示当前(a_x=3)的数有(i)个,(=2)的有(j)个,(=1)的有(k)个。
    则有方程

    [f[i][j][k]=frac {left( 1+f[i-1][j+1][k]*frac i n+f[i][j-1][k+1] * frac j n + f[i][j][k-1]* frac k n ight)}{frac {i+j+k} n} ]

    答案即为(f[cnt[3]][cnt[2]][cnt[1]])

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 310;
    
    double f[N][N][N];
    int n, a[N], cnt[5];
    
    int main() {
    	scanf("%d", &n);
    	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]), cnt[a[i]]++;
    	for(int i = 0; i <= n; ++i) {
    		for(int j = 0; j <= n; ++j) {
    			for(int k = 0; k <= n; ++k) {
    				if(i + j + k > n) continue;
    				if(!i && !j && !k) continue;
    				if(i > cnt[3]) continue;
    				if(j > cnt[3] + cnt[2]) continue;
    				if(k > cnt[3] + cnt[2] + cnt[1]) continue;
    				double p1 = 1.0 * i / n, p2 = 1.0 * j / n, p3 = 1.0 * k / n;
    				double p4 = 1.0 * (i + j + k) / n;
    				f[i][j][k] = (double)(1+(i?f[i-1][j+1][k]:0)*p1+(j?f[i][j-1][k+1]:0)*p2+(k?f[i][j][k-1]:0)*p3)/p4;
    			}
    		}
    	}
    	printf("%.10lf
    ", f[cnt[3]][cnt[2]][cnt[1]]);
    }
    

    K

    (SG)函数板子题,根据(SG)定理,只需要(sg(k))不为(0)就先手必胜。
    对于( ext{mex})运算我直接从第一个数开始枚举了...需要复杂度正确的话就需要写个主席树或者写个权值分块。复杂度是(O(nklog A))或者(O(nk sqrt A)),如果直接枚举最坏是(O(nkA))的但是跑不到,能过。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 100010;
    
    int sg[N], n, k, a[N], vis[N];
    
    int mex(int x) {
    	memset(vis, 0, sizeof(vis));
    	for(int i = 1; i <= n; ++i) {
    		if(x - a[i] >= 0) vis[sg[x - a[i]]] = 1;
    	}
    	for(int i = 0; i <= 100000; ++i) if(!vis[i]) return i;
    }
    
    int main() {
    	scanf("%d%d", &n, &k);
    	for(int i = 1; i <= n; ++i) {
    		scanf("%d", &a[i]);
    	}
    	for(int j = 1; j <= k; ++j) sg[j] = mex(j);
    	if(sg[k]) puts("First");
    	else puts("Second");
    }
    

    L

    (f[l][r])表示按策略取完区间([l,r])可以得到的分数。
    直接按他们的策略模拟即可,这个策略是有阶段性的。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N = 3100;
    
    ll f[N][N];
    int n, a[N];
    
    int main() {
    	scanf("%d", &n);
    	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    	for(int i = 1; i <= n; ++i) if(n & 1) f[i][i] = a[i]; else f[i][i] = -a[i];
    	for(int len = 2; len <= n; ++len) {
    		for(int l = 1; l <= n; ++l) {
    			int r = l + len - 1;
    			if((n - len) & 1) f[l][r] = min(f[l + 1][r] - a[l], f[l][r - 1] - a[r]);
    			else f[l][r] = max(f[l + 1][r] + a[l], f[l][r - 1] + a[r]);
    		}
    	}
    	printf("%lld
    ", f[1][n]);
    }
    

    M

    用前缀和优化一下转移即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N = 100010;
    const ll mod = 1e9 + 7;
    
    int n, k, a[N];
    ll f[101][N], sum[101][N];
    /*
     f[i][j]表示前i个人,一共分了j份。
     */
    int main() {
    	int cur = 1;
    	scanf("%d%d", &n, &k);
    	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    	sum[0][1] = f[0][1] = 1;
    	for(int i = 1; i <= k + 1; ++i) sum[0][i] = 1; 
    	for(int i = 1; i <= n; ++i) {
    		for(int j = 1; j <= k + 1; ++j) {
    			(f[i][j] = sum[i - 1][j] - sum[i - 1][max(j - a[i] - 1, 0)]) %= mod;
    			sum[i][j] = (f[i][j] + sum[i][j - 1]) % mod;
    		}
    	}
    	printf("%lld
    ", (f[n][k + 1] % mod + mod) % mod);
    /*	for(int i = 1; i <= n; ++i) {
    		for(int j = 1; j <= k + 1; ++j) printf("%d ", sum[i][j]);
    		puts("");
    	}*/
    }
    

    N

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N = 500;
    
    ll f[N][N], sum[N];
    int n, a[N], ans = 0;
    
    int main() {
    	scanf("%d", &n);
    	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]), sum[i] = sum[i - 1] + a[i];
    	memset(f, 0x3f, sizeof(f));
    	for(int i = 1; i <= n; ++i) f[i][i] = 0;
    	for(int len = 2; len <= n; ++len) {
    		for(int l = 1; l <= n; ++l) {
    			int r = l + len - 1;
    			if(r > n) break;
    			for(int k = l; k < r; ++k) {
    				f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + sum[r] - sum[l - 1]);
    			}
    		}
    	}
    	printf("%lld
    ", f[1][n]);
    }
    

    O

    状压dp。
    一开始写了很傻的(O(n^2 2^n))居然都过了,(O(n 2 ^n))的做法就是先枚举集合,然后统计出当前匹配了多少人(i),对(f[i][S])枚举转移点转移即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 22;
    const int mod = 1e9 + 7;
    
    int n, a[N][N];
    int f[N][1 << N];
    
    int main() {
    	scanf("%d", &n);
    	for(int i = 1; i <= n; ++i) {
    		for(int j = 1; j <= n; ++j) scanf("%d", &a[i][j]);
    	}
    	f[0][0] = 1;
    	for(int S = 1; S < (1 << n); ++S) {
    		int i = 0;
    		for(int j = 0; j < n; ++j) if((S >> j) & 1) ++i;
    		for(int j = 0; j < n; ++j) {
    			if(((S >> j) & 1) && a[i][j + 1]) (f[i][S] += f[i - 1][S ^ (1 << j)]) %= mod;
    		}
    	}
    	printf("%d
    ", f[n][(1 << n) - 1]);
    }
    

    P

    (f[i][0/1])表示节点(i)涂成黑色/白色的方案数。
    有方程(f[i][1]=prod_{jin son(i)} (f[j][0]+f[j][1]),f[i][0]=sum_{jin son(i)} f[j][1])

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const ll mod = 1e9 + 7;
    const int N = 100010;
    
    int n;
    ll f[N][2];
    // f[i][0/1]表示将该节点涂成黑色/白色的方案数
    int cnt, head[N];
    struct edge { int to, nxt; } e[N << 1];
    
    void ins(int u, int v) {
    	e[++cnt] = (edge) { v, head[u] };
    	head[u] = cnt;
    }
    
    void dfs(int u, int fa) {
    	f[u][0] = f[u][1] = 1;
    	for(int i = head[u]; i; i = e[i].nxt) {
    		int v = e[i].to;
    		if(v == fa) continue;
    		dfs(v, u);
    		(f[u][0] *= f[v][1]) %= mod;
    		(f[u][1] *= (f[v][0] + f[v][1]) % mod) %= mod;
    	}
    }
    
    int main() {
    	scanf("%d", &n);
    	for(int u, v, i = 1; i < n; ++i) {
    		scanf("%d%d", &u, &v);
    		ins(u, v), ins(v, u);
    	}
    	dfs(1, 0);
    	printf("%lld
    ", (f[1][0] + f[1][1]) % mod);
    }
    

    Q

    用BIT维护一下前缀(max)转移即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    const int N = 200010;
    
    int n, a[N], h[N];
    ll c[N];
    #define lowbit(i) (i & -i)
    void add(int x, ll v) {
    	for(int i = x; i <= n; i += lowbit(i)) c[i] = max(c[i], v);
    }
    ll query(int x) {
    	ll ans = 0;
    	for(int i = x; i; i -= lowbit(i)) ans = max(ans, c[i]);
    	return ans;
    }
    
    int main() {
    	scanf("%d", &n);
    	for(int i = 1; i <= n; ++i) scanf("%d", &h[i]);
    	for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    	for(int i = 1; i <= n; ++i) {
    		add(h[i], query(h[i]) + a[i]);
    	}
    	printf("%lld
    ", query(n));
    }
    

    R

    和bzoj cow relays一样。
    把矩阵乘法写成类似floyd那样,用矩阵快速幂维护即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    const int N = 60;
    const ll mod = 1e9 + 7;
    
    int n;
    ll K;
    struct mat {
    	ll d[N][N];
    	mat() {memset(d, 0, sizeof(d));}
    	mat operator * (mat &x) {
    		mat ans;
    		for(int k = 1; k <= n; ++k) {
    			for(int i = 1; i <= n; ++i) {
    				for(int j = 1; j <= n; ++j) {
    					(ans.d[i][j] += d[i][k] * x.d[k][j] % mod) %= mod;
    				}
    			}
    		}
    		return ans;
    	}
    } A[65];
    
    int main() {
    	scanf("%d%lld", &n, &K);
    	for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j) scanf("%lld", &A[0].d[i][j]);
    	for(ll i = 1; (1LL << i) <= K; ++i) {
    		A[i] = A[i - 1] * A[i - 1];
    	}
    	mat ans;
    	for(int i = 1; i <= n; ++i) ans.d[i][i] = 1;
    	for(ll i = 0; (1LL << i) <= K; ++i) 
    		if((K >> i) & 1LL) ans = ans * A[i];
    	ll sum = 0;
    	for(int i = 1; i <= n; ++i) {
    		for(int j = 1; j <= n; ++j) (sum += ans.d[i][j]) %= mod;
    	}
    	printf("%lld
    ", sum);
    }
    

    S

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    const int N = 100010;
    const int mod = 1e9 + 7;
    
    char s[N];
    int d, n, a[N];
    int f[10010][110];
    
    int dfs(int cnt, int limit, int lead, int s) {
    	if(!cnt) return !s && !lead;
    	if(~f[cnt][s] && !limit && !lead) return f[cnt][s];
    	int sum = 0, ed = limit ? a[cnt] : 9;
    	for(int i = 0; i <= ed; ++i) {
    		(sum += dfs(cnt - 1, limit && (i == ed), lead && (!i), (s + i) % d)) %= mod;
    	}
    	if(!limit && !lead) return f[cnt][s] = sum;
    	return sum;
    }
    
    int main() {
    	memset(f, -1, sizeof(f));
    	scanf("%s%d", s + 1, &d);
    	n = strlen(s + 1);
    	for(int i = 1; i <= n; ++i) a[i] = s[i] - '0';
    	reverse(a + 1, a + n + 1);
    	printf("%lld
    ", dfs(n, 1, 1, 0));
    }
    

    T

    (f[i][j])表示填在第(i)位的数在已填入的数中排名为(j)
    考虑这样子设计状态为什么是对的,如果第二维表示的是填入的数是(j),那么并不能知道前面(i-1)个数具体填的是什么(因为排列不可重,所以不能填重复的数),也就无从转移。
    设成在已填入数中排名为(j),这样知道的是相对关系,所以可转移点也就确定了(相对小于它的点)。
    对于(s[i]='<'),有(f[i][j]=sum_{k< j} f[i-1][k])
    对于(s[i]='>'),有(f[i][j]=sum_{jle k< i}f[i-1][k])
    维护一下前缀和即可(O(n^2))转移。
    设计状态的时候设计成相对关系有时候很有用。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N = 3010;
    const int mod = 1e9 + 7;
    
    char s[N];
    int f[N][N], sum[N], n;
    
    /*
    f[i][j]表示位置i,然后位置i的数在已填入的数中排名为j
    '>' f[i][j] = sum_{k < j} f[i - 1][k] 
    '<' f[i][j] = sum_{k >= j} f[i - 1][k]
     */
    
    int main() {
    	scanf("%d", &n);
    	scanf("%s", s + 2);
    	f[1][1] = sum[1] = 1;
    	for(int i = 2; i <= n; ++i) {
    		for(int j = 1; j <= i; ++j) {
    			if(s[i] == '<') (f[i][j] += sum[j - 1]) %= mod;
    			else (f[i][j] += sum[i - 1] - sum[j - 1] + mod) %= mod;
    		}
    		for(int j = 1; j <= i; ++j) sum[j] = (sum[j - 1] + f[i][j]) % mod;
    	}
    	printf("%d
    ", sum[n]);
    }
    

    U

    状压dp。枚举子集转移即可。
    (f[S]=sum f[S_1]+val[Soplus S_1])其中(S_1)(S)的子集。
    (val[S])可以(O(n^22^n))处理出来。
    复杂度为(O(3^n+n^22^n))

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N = 17;
    
    int n, a[N][N], b[N], cnt;
    ll f[1 << N], val[1 << N];
    
    int main() {
    	scanf("%d", &n);
    	for(int i = 0; i < n; ++i) {
    		for(int j = 0; j < n; ++j) scanf("%d", &a[i][j]);
    	}
    	for(int S = 1; S < (1 << n); ++S) {
    		cnt = 0;
    		for(int i = 0; i < n; ++i) {
    			if((S >> i) & 1) b[++cnt] = i;
    		}
    		for(int i = 1; i <= cnt; ++i) {
    			for(int j = i + 1; j <= cnt; ++j) val[S] += a[b[i]][b[j]];
    		}
    	}
    	for(int S = 1; S < (1 << n); ++S) {
    		for(int S1 = S; S1; S1 = (S1 - 1) & S) {
    			f[S] = max(f[S], f[S ^ S1] + val[S1]);
    		}
    	}
    	printf("%lld
    ", f[(1 << n) - 1]);
    }
    

    V

    W

    线段树优化dp。
    因为如果选了一个点,那么右端点在它之前的线段显然不会对这个点有影响,那么把那些线段的贡献都加上去之后取max转移即可。具体可见代码。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    const int N = 300010;
    const ll inf = 1e16;
    
    ll f[N];
    int n, m;
    struct tree { int l, r; ll mx, tag; } t[N << 2];
    struct line { int l, r; ll v; } a[N];
    
    #define lc (rt << 1)
    #define rc (rt << 1 | 1)
    void build(int l, int r, int rt) {
        t[rt].l = l; t[rt].r = r;
        if(l == r) return; int mid = (l + r) >> 1;
        build(l, mid, lc); build(mid + 1, r, rc);
    }
    void up(int rt) { t[rt].mx = max(t[lc].mx, t[rc].mx); }
    #define l (t[rt].l)
    #define r (t[rt].r)
    #define mid ((l + r) >> 1)
    void down(int rt) {
        if(t[rt].tag) {
            t[lc].mx += t[rt].tag; t[rc].mx += t[rt].tag;
            t[lc].tag += t[rt].tag; t[rc].tag += t[rt].tag;
            t[rt].tag = 0;
        }
    }
    void upd(int L, int R, ll c, int rt) {
        if(L <= l && r <= R) {
            t[rt].mx += c; t[rt].tag += c;
            return;
        }
        down(rt);
        if(L <= mid) upd(L, R, c, lc);
        if(R > mid) upd(L, R, c, rc);
        up(rt);
    }
    ll query(int L, int R, int rt) {
        if(L <= l && r <= R) return t[rt].mx;
        down(rt); ll ans = -inf;
        if(L <= mid) ans = max(ans, query(L, R, lc));
        if(R > mid) ans = max(ans, query(L, R, rc));
        return ans;
    }
    #undef l
    #undef r
    #undef mid
    #undef lc
    #undef rc
    
    bool operator < (line a, line b) { return a.r < b.r; }
    
    int main() {
        scanf("%d%d", &n, &m);
        build(1, n, 1);
        for(int i = 1; i <= m; ++i) {
            scanf("%d%d%lld", &a[i].l, &a[i].r, &a[i].v);
        }
        sort(a + 1, a + m + 1);
        int r = 1;
        for(int i = 1; i <= n; ++i) { 
            upd(i, i, query(1, i, 1), 1);
            while(a[r].r == i) {
                upd(a[r].l, a[r].r, a[r].v, 1);
                ++r;
            }
        }
        printf("%lld
    ", max(0LL, query(1, n, 1)));
    }
    

    X

    (w+s)升序排序然后类似背包转移即可。
    这个排序的贪心可以用排序不等式证明。(也可以感性理解)

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 1010;
    typedef long long ll;
    
    int n;
    ll f[N][N * 30];
    struct Node { int w, s; ll v; } a[N];
    /*
     设f[i][j]表示前i个,然后总重量为j的最大答案。
     */
    
    bool operator < (Node a, Node b) {
    	if(a.s + a.w == b.s + b.w) return a.s < b.s; 
    	return a.s + a.w < b.s + b.w; 
    }
    
    int main() {
    	scanf("%d", &n);
    	for(int i = 1; i <= n; ++i) {
    		scanf("%d%d%lld", &a[i].w, &a[i].s, &a[i].v);
    	}
    	sort(a + 1, a + n + 1);
    	for(int i = 1; i <= n; ++i) {
    		for(int j = 0; j <= a[n].s + a[n].w; ++j) f[i][j] = max(f[i][j], f[i - 1][j]);
    		for(int j = 0; j <= a[i].s; ++j) f[i][j + a[i].w] = max(f[i][j + a[i].w], f[i - 1][j] + a[i].v);
    	}
    	ll ans = 0;
    	for(int i = 0; i <= a[n].s + a[n].w; ++i) ans = max(ans, f[n][i]);
    	printf("%lld
    ", ans);
    }
    

    Y

    计数dp。首先一个没有任何障碍的(h imes w)的网格图从((1,1))走到((h,w))的方案数为(C^{h-1}_{h+w-2})(考虑一共要走(h-1)次向下,(w-1)次向右,一共(h+w-2)次操作)
    (f[i])表示仅经过(i)个黑点且目前位于第(i)个黑点的方案数。
    将黑点按(x)为第一关键字,(y)为第二关键字升序排序。
    (f[i]=C^{x_i-1}_{x_i+y_i-2}-sum_{j<i,x_ile x_j,y_ile y_j}f[j] imes C^{x_i-x_j}_{x_i-x_j+y_i-y_j})
    钦定((h,w))为第(n+1)个黑点,那么答案就是(f[n+1])

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    const ll mod = 1e9 + 7;
    const int N = 200010;
    ll fac[N], inv[N], f[N];
    int h, w, n;
    struct Node { int x, y; } a[N];
    
    ll power(ll a, ll b) {
    	ll ans = 1;
    	while(b) {
    		if(b & 1) ans = ans * a % mod;
    		a = a * a % mod; b >>= 1;
    	} return ans;
    }
    
    bool operator < (Node a, Node b) { 
    	if(a.x == b.x) return a.y < b.y;
    	return a.x < b.x;
    }
    
    ll C(int n, int m) {
    	return fac[m] * inv[n] % mod * inv[m - n] % mod;
    }
    
    int main() {
    	scanf("%d%d%d", &h, &w, &n);
    	fac[0] = inv[0] = 1;
    	for(int i = 1; i < N; ++i) {
    		fac[i] = 1LL * fac[i - 1] * i % mod;
    		inv[i] = power(fac[i], mod - 2);
    	}
    	for(int i = 1; i <= n; ++i) {
    		scanf("%d%d", &a[i].x, &a[i].y);
    	}
    	sort(a + 1, a + n + 1);
    	a[n + 1] = (Node) { h, w };
    	for(int i = 1; i <= n + 1; ++i) {
    		f[i] = C(a[i].x - 1, a[i].x + a[i].y - 2);
    		for(int j = 1; j < i; ++j) {
    			if(a[j].x <= a[i].x && a[j].y <= a[i].y) {
    				f[i] -= C(a[i].x - a[j].x, a[i].x - a[j].x + a[i].y - a[j].y) * f[j] % mod;
    				(f[i] += mod) %= mod;
    			}
    		}
    	}
    	printf("%lld
    ", f[n + 1]);
    }
    
  • 相关阅读:
    ACM第六周竞赛题目——A LightOJ 1317
    数学概念——J
    数学概念——I
    数学概念——D 期望
    数学概念——A 几何概型
    数学概念——E 期望(经典问题)
    数学概念——F 概率(经典问题)birthday paradox
    数学概念——H 最美素数
    数学概念——G 最大公约数
    UVa1328
  • 原文地址:https://www.cnblogs.com/henry-1202/p/11569364.html
Copyright © 2011-2022 走看看