湘潭邀请赛 2017 题解
E :Partial Sum
每次取任意两个不同的前缀,取过之后不能再取,最多取M次
将前缀和排序,优先取最大,最小就行了
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define ls i<<1 #define rs ls | 1 #define mid ((ll+rr)>>1) #define pii pair<int,int> #define MP make_pair typedef long long LL; const long long INF = 1e18+1LL; const double Pi = acos(-1.0); const int N = 5e5+10, M = 1e3+20, mod = 1e9+7,inf = 2e9; int n,m,c,sum[N],a[N],b[N]; int main() { while(scanf("%d%d%d",&n,&m,&c)!=EOF) { sum[0] = 0; int cnt =0; b[++cnt] = 0; for(int i = 1; i <= n; ++i) { scanf("%d",&a[i]); sum[i] = sum[i-1] + a[i]; b[++cnt] = sum[i]; } sort(b+1,b+cnt+1); int head = 1, tail = cnt; LL ans = 0; for(int i = 1; i <= m; ++i) { int f = b[head++]; int s = b[tail--]; if(abs(s-f)-c >= 0) { ans += abs(s-f)-c; } else break; } printf("%lld\n",ans); } return 0; }
F:Longest Common Subsequence
将a数组离散化,枚举三元素,n^3
求LCS,花费n*3,现在总体复杂度是n^4的
求LCS这步可以 优化,我们预处理吃nex[i][c],当前i位置后面第一个c在哪里
就可以在2^3下O(1)求出LCS了
有个坑的地方就是 a[i]可能会大于m,wa了很久
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define ls i<<1 #define rs ls | 1 #define mid ((ll+rr)>>1) #define pii pair<int,int> #define MP make_pair typedef long long LL; const long long INF = 1e18+1LL; const double Pi = acos(-1.0); const int N = 2e2+10, M = 1e3+20, mod = 1e9+7,inf = 2e9; LL n,m,a[N],c,b[N],nex[N][N]; LL v[N]; LL f[N]; LL query(LL x) { return lower_bound(b+1,b+c+1,x) - b; } int main() { while(scanf("%lld%lld",&n,&m)!=EOF) { for(int i = 1; i <= n; ++i) scanf("%lld",&a[i]),b[i] = a[i]; sort(b+1,b+n+1); c = unique(b+1,b+n+1) - b - 1; for(int i = c; i >= 1; --i) { if(b[i] > m) c--; else break; } for(int i = 0; i <= 5; ++i) f[i] = 0; for(int i = 1; i <= n; ++i) a[i] = query(a[i]); memset(nex,0,sizeof(nex)); memset(v,0,sizeof(v)); for(int i = 0; i <= n; ++i) { for(int j = 1; j <= c; ++j) { for(int k = i+1; k <= n; ++k) { if(a[k] == j) { nex[i][j] = k; break; } } } } for(int i = 1; i <= c; ++i) v[i] = 1; v[c+1] = m - c; for(int i = 1; i <= c+1; ++i) { for(int j = 1; j <= c+1; ++j) { for(int k = 1; k <= c+1; ++k) { int fi = 0, se = 0, th = 0; for(int C = 1; C < 8; ++C) { int now = 0; if(C&(4)) { if(!nex[now][i]) {continue;} else now = nex[now][i]; } if(C&(2)) { if(!nex[now][j]) {continue;} else now = nex[now][j]; } if(C&(1)) { if(!nex[now][k]) {continue;} else now = nex[now][k]; } if(C == 7) fi = 1; else if(C == 6 || C == 5 || C == 3) se = 1; else if(C)th = 1; } if(fi){ f[3] += v[i]*v[j]*v[k]; } else if(se) { f[2] += v[i]*v[j]*v[k]; } else if(th) { f[1] += v[i]*v[j]*v[k]; } else { f[0] += v[i]*v[j]*v[k]; } } } } for(int i = 0; i < 3; ++i) cout<<f[i]<<" "; cout<<f[3]<<endl; } return 0; }
H:Highway
求一次树的直径
再从直径的两个端点做一次bfs最短路
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define ls i<<1 #define rs ls | 1 #define mid ((ll+rr)>>1) #define pii pair<int,int> #define MP make_pair typedef long long LL; const long long INF = 1e18+1LL; const double Pi = acos(-1.0); const int N = 1e5+10, M = 1e3+20, mod = 1e9+7,inf = 2e9; struct ss{ int to,next,c; }e[N * 2]; int head[N],t = 1; LL sum,mx[N],dp[N]; int from,q[N],vis[N],n; void add(int u,int v,int w) { e[t].to = v; e[t].next = head[u]; e[t].c = w; head[u] = t++; } void dfs(int u,int f,LL dep) { if(dep > sum) { sum = dep; from = u; } for(int i = head[u]; i; i = e[i].next) { int to = e[i].to; if(to == f) continue; dfs(to,u,dep + e[i].c); } } void bfs(int u) { int l = 0,r = 1; q[++l] = u; dp[u] = 0; while(l <= r) { int k = q[l++]; for(int i = head[k]; i; i = e[i].next) { int to = e[i].to; if(vis[to]) continue; dp[to] = dp[k] + e[i].c; vis[to] = 1; q[++r] = to; } } for(int i = 0; i <= n; ++i) vis[i] = 0,mx[i] = max(mx[i],dp[i]); } void init() { t =1; memset(head,0,sizeof(head)); for(int i =0; i <= n; ++i) vis[i] = 0,mx[i] = 0; } int main() { while(scanf("%d",&n)!=EOF) { init(); for(int i = 1; i < n; ++i) { int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,c); add(b,a,c); } sum = 0; dfs(1,0,0); int fi = from; sum = 0; dfs(from,0,0); LL ans = sum; bfs(fi); bfs(from); for(int i = 1; i <= n; ++i) { if(i != fi && i != from) ans += mx[i]; } printf("%lld\n",ans); } return 0; }
J:Similar Subsequence
设 f(i, j, x, y) 表示分别匹配到 ai 和 bj,数字的上界和下界分别是 x 和 y 的方案数。
注意到 x 和 y 总有一个等于 bj,所以状态数是 nm2 的。
转移就是枚举 ai+1 匹配的是 bk,要求 bk 落在 [x, y] 中。
这个可 以用树状数组优化。复杂度是 O(nm2 log m).题解来自icpccamp;
#include<stdio.h> #include<string.h> #include<math.h> #include<iostream> #include<vector> #include<queue> #include<map> #include<set> #include<algorithm> using namespace std; typedef long long ll; typedef double db; const int mod=1000000007; int t,n,m; int A[25],B[510]; int dp[25][510][510]; int a[25][510][510]; int lowbit(int x) { return x&(-x); } void add(int id,int bd,int x,int v) { while(x) { a[id][bd][x]+=v; if(a[id][bd][x]>=mod) a[id][bd][x]-=mod; if(a[id][bd][x]<0) a[id][bd][x]+=mod; x-=lowbit(x); } } int sum(int id,int bd,int x) { int res=0; while(x<=m) { res+=a[id][bd][x]; if(res>=mod) res-=mod; x+=lowbit(x); } return res; } int main() { #ifdef Haha //freopen("in.in","r",stdin); #endif // Haha while(scanf("%d%d",&n,&m)!=EOF) { for(int i=1; i<=n; i++) scanf("%d",&A[i]); for(int i=1; i<=m; i++) scanf("%d",&B[i]); memset(dp,0,sizeof(dp)); memset(a,0,sizeof(a)); if(A[1]==0) add(1,m,m,1); else add(1,1,m,1); for(int i=1; i<=n; i++) { for(int j=1; j<=m; j++) { for(int k=1; k<=m; k++) { dp[i][j][k]=sum(i,k,B[j]); //printf("%d %d %d %d\n",i,j,k,dp[i][j][k]); } if(i==1) continue; for(int k=1; k<=m; k++) { if(dp[i-1][j][k]==0) continue; int L,R; if(A[i-1]==0) L=B[j],R=k; else L=k,R=B[j]; if(L>R) continue; if(A[i]==0) { add(i,R,R,dp[i-1][j][k]); add(i,R,L-1,-dp[i-1][j][k]); } else { add(i,L,R,dp[i-1][j][k]); add(i,L,L-1,-dp[i-1][j][k]); } } } } int ans=0; for(int i=1; i<=m; i++) { for(int j=1; j<=m; j++) { ans+=dp[n][i][j]; if(ans>=mod) ans-=mod; } } printf("%d\n",ans); } return 0; }