A题
对于这个关系式,xi和yi都是下标,只要记录一下每个下标被加或减的次数,把相加次数最少的分配给最小的值就可以
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 20; typedef long long ll; int a[N],b[N],n,m,x,y; ll ans; void solve() { scanf("%d%d",&n,&m); for(int i = 1;i <= n; ++i) { scanf("%d",&a[i]); } for(int i = 1;i <= m; ++i) scanf("%d%d",&x,&y),b[x]++,b[y]--; sort(a + 1,a + n + 1); sort(b + 1,b + n + 1); for(int i = 1;i <= n; ++i) ans += 1LL * a[i] * b[n - i + 1]; printf("%lld\n",ans); } int main() { int t = 1; //scanf("%d",&t); while(t--) solve(); return 0; }
B题
可以看出是一个树形DP,f[i][j]表示根为i的子树,子树重量之和为j的最小的l、r之和
很明显重量直接从1-2500会比较慢,所以状态枚举的时候用子树的节点个数对枚举状态进行了缩小
然后暴力更新即可
#include<bits/stdc++.h> using namespace std; const int N = 55; int a[N],lt[N],rt[N],siz[N]; int f[N][N *N],tmp[N * N]; int n,m,u,x,y; void dfs(int x) { //cout<<x<<endl; siz[x] = 1; if(lt[x]) { dfs(lt[x]); dfs(rt[x]); siz[x] += siz[lt[x]] + siz[rt[x]]; //cout<<siz[x]<<' '<<x<<endl; memset(tmp, 0x3f, sizeof(tmp)); for(int i = siz[lt[x]] * a[1];i <= siz[lt[x]] * a[m]; ++i) for(int j = siz[rt[x]] * a[1];j <= siz[rt[x]] * a[m]; ++j) { int cal = (i + j) / __gcd(i,j); tmp[i + j] = min(tmp[i + j],f[lt[x]][i] + f[rt[x]][j] + cal); //cout<<i + j<<' '<<tmp[i][j]<<endl; }//将他单独计算出来可以有效减少重复的枚举 for(int i = (siz[x] - 1) * a[m]; i >= (siz[x] - 1) * a[1]; --i) for(int j = 1;j <= m; ++j) f[x][i + a[j]] = min(f[x][i + a[j]],tmp[i]); } else for(int i = 1; i <= m; ++i) f[x][a[i]] = 0; } int main() { scanf("%d%d",&n,&m); memset(f,0x3f, sizeof(f)); for(int i = 1;i <= m; ++i) scanf("%d",&a[i]); sort(a + 1,a + m + 1); while(~scanf("%d%d%d",&u,&x,&y)) { lt[u] = x; rt[u] = y; } dfs(1); int ans = 0x3f3f3f3f; for(int i = n * a[1];i <= n * a[m]; ++i) ans = min(ans,f[1][i]);//cout<<f[1][i]<<' '; printf("%d\n",ans); return 0; }