A.
题意:求去掉d物品后容量为e最大背包。每个物品有三种属性,权值、容量、数量。
#include <bits/stdc++.h> using namespace std; const int V=1000, N=1005; void zop(int *d, int w, int v) { for(int i=V; i>=v; --i) d[i]=max(d[i], d[i-v]+w); } void cmp(int *d, int w, int v) { for(int i=v; i<=V; ++i) d[i]=max(d[i], d[i-v]+w); } void dcp(int *d, int w, int v, int c) { if(c*v>=V) { cmp(d, w, v); return; } for(int i=1; i<=c; ++i) zop(d, w, v); } int l[N][V+5], r[N][V+5], a[N], b[N], c[N], n; int main() { scanf("%d", &n); for(int i=1; i<=n; ++i) scanf("%d%d%d", &a[i], &b[i], &c[i]); for(int i=1; i<=n; ++i) memcpy(l[i], l[i-1], sizeof l[i-1]), dcp(l[i], b[i], a[i], c[i]); for(int i=n; i>=1; --i) memcpy(r[i], r[i+1], sizeof r[i+1]), dcp(r[i], b[i], a[i], c[i]); int q; scanf("%d", &q); while(q--) { int d, e, ans=0; scanf("%d%d", &d, &e); ++d; for(int i=0; i<=e; ++i) ans=max(ans, l[d-1][i]+r[d+1][e-i]); printf("%d\n", ans); } return 0; }
考场sb没话说,只拿了80分暴力分= =
题解:背包其实是可以合并的,因此预处理前面的背包和后面的背包,查询的时候合并即可。(妈呀谁来教我多重背包怎么优化啊,二进制分组貌似并不能计算出每一个背包的值啊= =
B.
题意:对于博弈树,定义最小黑方胜集合为最小的叶子节点集合,满足集合内的叶子如果确定是胜利,那么黑方必胜。最小白方胜集合同理。现在给出博弈树,求黑方先手的情况下既在最小白方胜集合的点也在最小黑方胜集合的点。
#include <bits/stdc++.h> using namespace std; const int N=200005; struct E { int next, to; }e[N<<1]; int ihead[N], cnt, n, sz[N]; void add(int x, int y) { e[++cnt]=(E){ihead[x], y}; ihead[x]=cnt; e[++cnt]=(E){ihead[y], x}; ihead[y]=cnt; } int dfs1(int x, int f, bool dep=0) { int ret=dep?0:~0u>>1, c=1; for(int i=ihead[x]; i; i=e[i].next) if(e[i].to!=f) { c=0; int y=e[i].to; int s=dfs1(y, x, !dep); if(dep) ret+=s; else ret=min(ret, s); } if(c) return sz[x]=1; return sz[x]=ret; } void dfs2(int x, int f, int *ok, bool dep=0) { int c=1; for(int i=ihead[x]; i; i=e[i].next) if(e[i].to!=f) { c=0; int y=e[i].to; if(dep) dfs2(y, x, ok, !dep); else { if(sz[y]==sz[x]) dfs2(y, x, ok, !dep); } } if(c) ok[x]=1; } void work(int *ok, int rt) { dfs1(rt, -1); dfs2(rt, -1, ok); } int vis[2][N]; int main() { scanf("%d", &n); for(int i=2; i<=n; ++i) { int x; scanf("%d", &x); add(x, i); } work(vis[0], 1); add(n+1, 1); work(vis[1], n+1); int ans=0, tot=0, xo=0; for(int i=1; i<=n; ++i) if(vis[0][i] && vis[1][i]) { if(!ans) ans=i; tot++; xo^=i; } printf("%d %d %d\n", ans, tot, xo); return 0; }
推了一下结论,首先根据博弈论,当前节点必胜当且仅当后继节点至少有一个必败态,必败当且仅当后继节点全为必胜态。可以发现当前节点要胜时,后继只需要一个必败态,而不可能大于两个必败态(多余两个只会增大集合)。因此按照博弈论的知识对树dfs两次就好辣。求白方必胜我们只需要新加一个节点连边黑方先手的节点就行啦。
C.
D.
题意:给出一个序列A,求一个序列B使得max{|B[i]-A[i]|}最小。
#include <bits/stdc++.h> using namespace std; const int N=5000005; typedef long long ll; int a[N], n, Sa, Sb, Sc, Sd, mo; inline int F(int x) { x%=mo; return ((ll)Sa*x%mo*x%mo*x%mo + ((ll)Sb*x%mo*x%mo + ((ll)Sc*x%mo + Sd)%mo)%mo)%mo; } bool check(ll d) { ll pre=(~0ull>>1)+1; for(int i=1; i<=n; ++i) { if((ll)a[i]>=pre) pre=max((ll)a[i]-d, pre); else { if((ll)a[i]+d<pre) return 0; } } return 1; } int main() { scanf("%d%d%d%d%d%d%d", &n, &Sa, &Sb, &Sc, &Sd, &a[1], &mo); Sa%=mo; Sb%=mo; Sc%=mo; Sd%=mo; for(int i=2; i<=n; ++i) a[i]=(F(a[i-1])+F(a[i-2]))%mo; ll l=0, r=(ll)(~0u>>1)*2ll; while(l<=r) { ll mid=(l+r)>>1; if(check(mid)) r=mid-1; else l=mid+1; } printf("%lld\n", r+1); return 0; }
一开始看错题以为是sum= =后来发现是max。。。。。。
于是二分答案就行辣= =
然后发现题解是有一个结论= =差值最大的逆序对的差值除以二取上界= =。。。。(好像显然吧= =。。。
时间: 2024-11-07 02:18:45