[冬令营模拟]GTSG2018
上学期没有去 GTSG,于是今天老师让我们来做一下 GTSG2018 Day1 & Day3
Day1 在上午当成一场考试来搞了,Day3 由于锅太多而且 T3 玄学而被放到下午自学...
上午 100 + 45(老师放的是后 19 组原数据和一组 hack 数据,所以只有 40,现场的话应该是 45 )+ 80 = 225
T1 假面 faceless
n 个人,每个人有血量,q 次操作,现在有 2 种操作
1.指定一个人 x ,有 p 的概率扣他 1 滴血,一个人没有血,就死了
2.选出 k 个人 $a_1,a_2,...,a_k$ ,先去掉里面所有的死人,剩下的人中等概率选一个补魔
对于每个 2 操作求那 k 个人中每个人被补魔的概率,在所有操作后,求出每个人剩余血量的期望
$n \leq 200,q \leq 200000$ 操作 2 的次数不超过 $2000$
sol:第一问就是一个普及组的背包问题
第二问我们可以想到一个 $O(n^3)$ 的普及组做法
首先我们由第一问,可以得到一个数组 $d_i$ 表示第 i 个人死了的概率,$f_{(i,j)}$ 表示除了 i ,还有 j 个人活着的概率,
于是对于选出来的第 i 人,他被选到的概率为
$$(1 - d_i) \times \sum^{k-1}_{j=0} \frac{1}{j+1} \times f_{(i,j)}$$
$f_{(i,j)}$ 可以用一个数组 $g_{(i,j)}$ 辅助转移,令 $g_{(i,j)}$ 为前 i 个人有 j 个活着的概率
$$g_{(i,j)}=g_{(i-1,j)} \times d_i + g_{(i-1,j-1)} \times (1 - d_i)$$
然后发现这个 $g$ 的转移可以爱怎么转怎么转,「前 i 个人」这一维就是假的,对于当前每个人我们就当他是第 k 个人,强行转移一遍
这样是 $O(n^3)$ 的普及组做法
我们要想办法优化它
xjr:我会 FFT!
然而感觉不是很好写,而且我这种人蠢自带大常数... $O(n^2logn)$ 很可能只有暴力分
我们把 $g$ 的转移式倒过来,这样就不用每个数重新 dp 一次了
$$g_{(i-1,j)}=\frac{g_{(i,j)} - g{(i-1.j-1) \times (1 - d_i)} }{d_i}$$
我们可以 $O(n^2)$ D 过去 再 $O(n^2)$ D 回来
在确定死和确定没掉血的时候要特判一下
#include<bits/stdc++.h> #define LL long long using namespace std; inline LL read() { LL x = 0,cs = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-')cs = -cs; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * cs; } void write(LL x) { if(x < 0) putchar('-'), x = -x; if(x >= 10) write(x / 10); putchar('0' + x % 10); } const int maxn = 300, mod = 998244353; int n,m,k,b[maxn]; int id[maxn]; int dp[maxn][105],inv[maxn]; LL cs[maxn],cnt[maxn],f[maxn]; LL qpow(LL x, LL t) { LL ret = 1; while(t) { if(t & 1) ret = ret * x % mod; x = x * x % mod; t >>= 1; } return ret; } int main() { freopen("faceless.in","r",stdin); freopen("faceless.out","w",stdout); n = read(); for(int i = 1; i <= n; i++){dp[i][b[i]=read()] = 1;inv[i] = qpow(i, mod - 2);} m = read(); while(m--) { int op = read(); if(!op) { int to = read(),u = read(), v = read(); LL rate = (LL) u * qpow(v, mod - 2) % mod; LL nrate = (1 - rate + mod) % mod; for(int i = 0; i <= b[to]; i++) { if(i > 0) dp[to][i] = dp[to][i] * nrate % mod; if(i < b[to]) dp[to][i] = (dp[to][i] + dp[to][i + 1] * rate) % mod; } } else { k = read(); for(int i = 1; i <= k; i++) id[i] = read(); memset(cs, 0, sizeof(cs));memset(cnt,0,sizeof(cnt));memset(f,0,sizeof(f)); cs[0] = 1; for(int i = 1; i <= k; i++) for(int j = i; j >= 0; j--) { if(j)cs[j] = (((1 - dp[id[i]][0]) * cs[j - 1]) + dp[id[i]][0] * cs[j]) % mod; else (cs[j] *= dp[id[i]][0]) %= mod; } for(int i = 1; i <= k; i++) { if(dp[id[i]][0]) { int fm = qpow(dp[id[i]][0], mod - 2); for(int j = 0; j < k; j++) { if(j)cnt[j] = (cs[j] - (cnt[j - 1] * (1 - dp[id[i]][0]))) % mod * fm % mod; else cnt[j] = cs[j] * fm % mod; f[i] += inv[j + 1] * cnt[j] % mod; } } else for(int j = 0; j < k; j++) f[i] += cs[j + 1] * inv[j + 1] % mod; f[i] = ((f[i] %= mod) * (1 - dp[id[i]][0])) % mod; f[i] = (f[i] + mod) % mod; } for(int i = 1; i <= k; i++){write(f[i]);if(i != k)putchar(' ');} putchar('\n'); } } for(int i = 1; i <= n; i++) { LL sum = 0; for(int j = 1; j <= b[i]; j++) sum += (LL)j * dp[i][j] % mod; write(sum % mod); if(i!=n)putchar(' '); } cout<<endl; return 0; }
T2 暴力写挂 wronganswer
有 2 棵 n 个点的有边权的树,求
$$depth[i]+depth[j]-depth[LCA1(i,j)]-depth[LCA2(i,j)]$$
的最大值
sol:不会写,写暴力
45 分是送的 只需要学会 $O(1)$ 求 $LCA$
听说某 SD 大佬模拟退火过了 90 多 ... orz
upd:正解还没写出来,先口胡一下吧
观察式子,发现前 3 项是第一棵树上的,最后一项是第二棵树上的,好像不是很滋磁同时维护跨树的信息,所以我们枚举第二棵树的 LCA
然后我们要维护的就是第一棵树上 $u,v$ 到根的链并最大值
那么显然我们可以树分治,让 $u,v$ 在不同子树里,然后用可以合并的数据结构维护(类似线段树/可并堆/启发式合并)
但好像这些数据结构维护链并都要套一个树剖之类的东西,就多个 log
根据网上大老板的说法,极限数据要跑一分多钟
然后我们可以考虑用点分治/边分治
由于要合并,点分治好像不太行,叉太多
我们先多叉转二叉
现在我们要维护的就是一个边分树,维护
1.加入一个点
2.暴力
加入一个点并不能用线段树合并那种,因为并不能很快的钦定这个点要走到哪
于是我们先建一遍边分树,找出每个点的位置,然后假装没有建过,再动态建一遍即可
#include<bits/stdc++.h> #define LL long long using namespace std; inline int read() { int x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } inline LL LL_read() { LL x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } const int maxn = 400010; int n; int lg[maxn + maxn]; struct rua { int first[maxn],to[maxn << 1],nx[maxn << 1],cnt; LL val[maxn << 1]; inline void add(int u,int v,LL w) { to[++cnt] = v; nx[cnt] = first[u]; first[u] = cnt; val[cnt] = w; } inline void ins(int u,int v,LL w){add(u,v,w);add(v,u,w);} int ind[maxn],_tim; int fa[maxn],dep[maxn],st[maxn][23],reh[maxn],bl[maxn]; LL dis[maxn]; inline void dfs(int x,int fa){ reh[++_tim]=x;ind[x]=_tim;dep[x]=dep[fa]+1;for(int i=first[x];i;i=nx[i]){if(to[i]==fa)continue;dis[to[i]] = dis[x] + val[i];dfs(to[i],x);reh[++_tim]=x;}} void makelca() { for(int i=1;i<=_tim;i++)st[i][0] = reh[i]; lg[0] = -1; for(int i=1;i<=_tim;i++)lg[i] = lg[i >> 1] + 1; for(int i=1;i<=22;i++) for(int j=1;j+(1<<i)<=_tim;j++) { if(dep[st[j][i-1]]<dep[st[j+(1<<(i-1))][i-1]]) st[j][i]=st[j][i-1]; else st[j][i]=st[j+(1<<(i-1))][i-1]; } } inline int lca(int x,int y) { if(ind[x]>ind[y])swap(x,y); int k=lg[ind[y]-ind[x]+1],ans; if(dep[st[ind[x]][k]]<dep[st[ind[y]-(1<<k)+1][k]]) ans=st[ind[x]][k]; else ans=st[ind[y]-(1<<k)+1][k]; return ans; }/* int size[maxn]; inline void dfs1(int x) { size[x] = 1; for(int i=first[x];i;i=nx[i]) { if(to[i] == fa[x])continue; fa[to[i]] = x;dis[to[i]] = dis[x] + val[i];dep[to[i]] = dep[x] + 1; dfs1(to[i]);size[x] += size[to[i]]; } } inline void dfs2(int x,int col) { bl[x] = col; int k = 0; for(int i=first[x];i;i=nx[i]) if(to[i] != fa[x] && size[to[i]] > size[k])k = to[i]; if(!k)return; dfs2(k,col); for(int i=first[x];i;i=nx[i]) if(to[i] != fa[x] && k != to[i])dfs2(to[i],to[i]); } inline int lca(int x,int y) { while(bl[x] != bl[y]) { if(dep[bl[x]] < dep[bl[y]])swap(x,y); x = fa[bl[x]]; }return dep[x] < dep[y] ? x : y; } void dfs(int x){dfs1(x),dfs2(x,x);}*/ }g1,g2; int main() { freopen("wronganswer.in","r",stdin); freopen("wronganswer.out","w",stdout); n = read(); for(int i=2;i<=n;i++) { int u = read(),v = read(),w = LL_read(); g1.ins(u,v,w); }g1.dfs(1,0);g1.makelca(); for(int i=2;i<=n;i++) { int u = read(),v = read(),w = LL_read(); g2.ins(u,v,w); }g2.dfs(1,0);g2.makelca(); LL ans = -1e16; for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) { int lc1 = g1.lca(i,j),lc2 = g2.lca(i,j); if(g1.dis[i] + g1.dis[j] - g1.dis[lc1] - g2.dis[lc2] >= ans)ans = g1.dis[i] + g1.dis[j] - g1.dis[lc1] - g2.dis[lc2]; } printf("%lld\n",ans); }
#include<bits/stdc++.h> #define LL long long using namespace std; inline int read() { int x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } const int maxn = 366666+366666+50; int n,tot,sum;LL ans = -1e16; struct edge{int to,val;edge(){}edge(int u,int v){to = u,val = v;}}q[maxn]; vector<edge> G1[maxn],G2[maxn / 2]; LL dis[maxn],f[maxn][20],fl[8300010],fr[8300010]; int size[maxn],fson[maxn]={maxn},dep[maxn]; LL lp[8300010],rp[8300010]; int root[maxn / 2],fa[maxn << 1]; LL val[maxn << 1]; int ls[maxn << 1],rs[maxn << 1]; LL id[8300010],top,mxd; int l,r,o;edge e1,e2; inline void build(int x,int fa) { for(auto e : G1[x]) { if(e.to == fa)continue; dis[e.to] = dis[x] + e.val; build(e.to,x); } l = 1,r = 0; for(auto e : G1[x]) if(e.to != fa)q[++r] = e; while(l + 2 <= r) { o = ++tot; e1 = q[l++];e2 = q[l++]; G1[o].push_back(edge(e1.to,e1.val));G1[e1.to].push_back(edge(o,e1.val)); G1[o].push_back(edge(e2.to,e2.val));G1[e2.to].push_back(edge(o,e2.val)); q[++r] = edge(o,0); } vector<edge>().swap(G1[x]); for(;l <= r;l++) { G1[x].push_back(edge(q[l].to,q[l].val)); G1[q[l].to].push_back(edge(x,q[l].val)); } } inline void getdis(int x,int fa,int d) { for(auto e : G1[x]) { if(e.to == fa || e.to == -1)continue; f[e.to][d] = f[x][d] + e.val; getdis(e.to,x,d); } } inline void getedge(int x,int fa,int &sx,int &sy) { size[x] = 1; for(auto e : G1[x]) { if(e.to == fa || e.to == -1)continue; getedge(e.to,x,sx,sy);size[x] += size[e.to]; if(fson[e.to] < fson[sy])sx = x,sy = e.to; } fson[x] = abs(sum - size[x] * 2); } inline int solve(int x,int sig,int d) { if(sig == 1){dep[x] = d;return x;} int sx = 0,sy = 0,o = ++tot; getdis(x,x,d);sum = sig;getedge(x,x,sx,sy); for(auto &e : G1[sx]) { if(e.to == sy){val[o] = e.val;e.to = -1;break;} } for(auto &e : G1[sy]) { if(e.to == sx){e.to = -1;break;} } fa[ls[o] = solve(sx,sig - size[sy],d + 1)] = o; fa[rs[o] = solve(sy,size[sy],d + 1)] = o; return o; } inline int ins(int x) { int u = x,lst = x; for(int i=dep[x];i>=1;i--) { id[++top] = fa[x];fl[top] = fr[top] = -(1LL << 60); if(ls[fa[x]] == x)fl[top] = max(fl[top],dis[u] + f[u][i]),lp[top] = lst; if(rs[fa[x]] == x)fr[top] = max(fr[top],dis[u] + f[u][i]),rp[top] = lst; lst = top;x = fa[x]; }return top; } inline int merge(int x,int y) { if(!x || !y)return x + y; ans = max(ans,(fl[x] + fr[y] + val[id[x]]) / 2 - mxd); ans = max(ans,(fl[y] + fr[x] + val[id[x]]) / 2 - mxd); fl[x] = max(fl[x],fl[y]);fr[x] = max(fr[x],fr[y]); lp[x] = merge(lp[x],lp[y]);rp[x] = merge(rp[x],rp[y]); return x; } inline void dfs(int x,int fa,LL d) { root[x] = ins(x);ans = max(ans,dis[x] - d); for(auto e : G2[x]) { if(e.to == fa)continue; dfs(e.to,x,d + e.val);mxd = d; root[x] = merge(root[x],root[e.to]); } } int main() { tot = n = read();int u,v,w; for(int i=2;i<=n;i++) { u = read(),v = read(),w = read(); G1[u].push_back(edge(v,w)); G1[v].push_back(edge(u,w)); } for(int i=2;i<=n;i++) { u = read(),v = read(),w = read(); G2[u].push_back(edge(v,w)); G2[v].push_back(edge(u,w)); }build(1,1);solve(1,tot,0);dfs(1,1,0); cout<<ans<<endl; } /* 6 1 2 2 1 3 0 2 4 1 2 5 -7 3 6 0 1 2 -1 2 3 -1 2 5 3 2 6 -2 3 4 8 */
T3 青蕈领主 green
有一个长度为 n 的 1 到 n 的排列 $a_i$
我们称一个区间是「连续的」当且仅当这个区间的最大值减最小值小于等于这个区间长度 -1
现在给定以每个点为右端点的最长「连续」区间的长度(至少为 1 ,因为 1 个数显然是「连续」的),求有多少种不同的原序列
$n \leq 50000$
一个点有 $T (T \leq 100)$ 组数据,每组数据的 $n$ 都相同
sol:考场上猜了一个卷积,但不会卷,于是 80 分遗憾离场,但瞎猜的东西竟然对了...就很赚
一题上天系列,达成成就「终于有一次考得比 syf 高」
非常重要的一件事,这些最长「连续」区间要么两两不相交,要么一个是另一个的子区间
之后我们可以发现,一个「连续的」区间可以先看成一个点,然后再算上内部的变化方案(前两天学校数学课上讲的「捆绑」2333)
证明嘛,yy 一下
于是缩起来之后,满足原题条件的方案数就变成了,一个 n 个数的排列,除了整个是一个「连续」区间外,没有其它长度大于等于 2 的子区间是「连续」区间的排列有多少个
我们称上面描黑的条件为「条件 1」
记 $f_{(n)}$ 为 $n+1$ 个数的排列中满足 「条件 1」的有多少个
显然,$f_0=0,f_1=1$
然后我们考虑如何得到 $f_n$,这个只需要考虑怎么把 $n+1$ 插进去就可以了
1.如果前 $n$ 个数组成的排列满足「条件 1」,我们只要不把 $n+1$ 插在 $n$ 旁边就可以了,有 $n-1$ 种插法,方案数是 $(n-1) \times f_{(n-1)}$
2.如果不满足,那一定有且仅有一个子区间是「连续」的,因为如果有多个子区间「连续」,$n+1$ 插进去还是不合法,这样我们就要枚举那个子区间有多长,那方案数就是 $\sum^{n-2}_{i=2}f_i \times f_{(n-i)}$ 因为子区间不能是原区间而且至少长度为 2
于是我们就猜到了 $$f_n=(n-1) \times f_{(n-1)} + \sum^{n-2}_{i=2}(i-1) \times f_i \times f_{(n-i)}$$
于是我们分治 FFT 就可以做了
于是不会分治 FFT
#开始自闭
#include<bits/stdc++.h> #define LL long long using namespace std; inline int read() { int x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } const int maxn = 50010,mod = 998244353,G = 3; int n,l[maxn]; LL poly[maxn]; inline LL skr(LL x,LL t) { LL res = 1; while(t) { if(t & 1)res = res * x % mod; x = x * x % mod; t = t >> 1; }return res; } int cnt[maxn]; int st[maxn]; int main() { freopen("green.in","r",stdin); freopen("green.out","w",stdout); int T = read();n = read(); poly[0] = 1;poly[1] = 2; for(int i=2;i<n;i++) { LL sig = 0; for(int j=2;j<=i-2;j++)(sig += ((poly[j] * poly[i - j]) % mod) * (j - 1)) %= mod; poly[i] = (sig + ((i - 1) * poly[i - 1]) % mod) % mod; } while(T--) { LL ans = 1,top = 0; for(int i=1;i<=n;i++)l[i] = read(),cnt[i] = 0; if(l[n] != n){puts("0");continue;} int NA = 0; for(int i=n;~i;i--) { while(top && st[top] - l[st[top]] >= i)cnt[st[--top]]++; if(top && i - l[i] < st[top] - l[st[top]]){NA = 1;break;} st[++top] = i; } if(NA){puts("0");continue;} for(int i=1;i<=n;i++)(ans *= poly[cnt[i]]) %= mod; printf("%lld\n",ans); } }
#include<bits/stdc++.h> #define LL long long using namespace std; #define int long long const int mod = 998244353,G = 3,maxn = 500100; inline int read() { int x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } int n,L; inline int skr(int x,int k) { int re = 1; for (; k; k >>= 1, x = x * x % mod) if (k & 1) re = re * x % mod; return re; } int poly[maxn],lg[maxn]; int ca[maxn],cb[maxn]; int R[maxn]; inline void init_ntt(int n,int L){for(int i=0;i<n;i++)R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));} inline void ntt(int *a,int type,int n) { for(int i=0;i<n;i++)if(i < R[i])swap(a[i],a[R[i]]); for(int i=1;i<n;i<<=1) { int wn = skr(G,(mod - 1) / (i << 1)); if(type == -1)wn = skr(wn,mod - 2); for(int j=0;j<n;j+=(i<<1)) { int w = 1; for(int k=0;k<i;k++,w=1LL*w*wn%mod) { int x = a[j + k],y = 1LL * w * a[i + j + k] % mod; a[j + k] = (((x + y) % mod) + mod) % mod; a[i + j + k] = (((x - y) % mod) + mod) % mod; } } } if(type == -1) { int inv = skr(n,mod - 2); for(int i=0;i<n;i++)a[i] = 1LL * a[i] * inv % mod; } } inline void mul(int *a,int *b,int n) { ntt(a,1,n);ntt(b,1,n); for(int i=0;i<n;i++)(a[i] *= b[i]) %= mod; ntt(a,-1,n); } inline void CDQ_ntt(int *a,int l,int r) { if(l == r) { (a[l] += (((l - 1) * a[l - 1]) % mod)) %= mod; return; } int mid = (l + r) >> 1; CDQ_ntt(a,l,mid);int qi = mid - l; for(int i=0;i<=qi;i++)ca[i] = a[i + l]; for(int i=0;i<=qi;i++)cb[i] = a[i + l] * (l + i - 1) % mod; int cn = 0,cl = 0;for(cn=1;cn<=(qi<<1);cn<<=1)cl++; init_ntt(cn,cl); fill(ca + qi + 1, ca + cn , 0); fill(cb + qi + 1, cb + cn , 0); //ntt(ca,1,n),ntt(cb,1,n); //for(int i=0;i<n;i++)(ca[i] *= cb[i]) % mod; mul(ca,cb,cn); for(int i=max(l+l,mid+1);i<=r;i++)a[i] += ca[i - l - l]; if(l == 2){CDQ_ntt(a,mid+1,r);return;} int qk = min(l - 1,r - l); for(cn = 1,cl = 0;cn <= qi + qk;cn <<= 1)cl++; init_ntt(cn,cl); for(int i=2;i<=qk;i++)ca[i - 2] = a[i]; for(int i=0;i<=qi;i++)cb[i] = a[i + l]; fill(ca + qk - 1, ca + cn , 0); fill(cb + qi + 1, cb + cn , 0); mul(ca,cb,cn); for(int i=mid+1;i<=r;i++)(a[i] += (ca[i - l - 2] * (i - 2))) %= mod; CDQ_ntt(a,mid+1,r); } int cnt[maxn]; int st[maxn],l[maxn]; signed main() { int T = read();n = read(); poly[0] = 1;poly[1] = 2; if(n > 2)CDQ_ntt(poly,2,n - 1); int m = n; while(T--) { LL ans = 1,top = 0; for(int i=1;i<=m;i++)l[i] = read(),cnt[i] = 0; if(l[m] != m){puts("0");continue;} int NA = 0; for(int i=m;~i;i--) { while(top && st[top] - l[st[top]] >= i)cnt[st[--top]]++; if(top && i - l[i] < st[top] - l[st[top]]){NA = 1;break;} st[++top] = i; } if(NA){puts("0");continue;} for(int i=1;i<=m;i++)(ans *= poly[cnt[i]]) %= mod; printf("%lld\n",ans); } }
Day3
T1 混合果汁 juice
有 n 种果汁,m 个人,第 i 种果汁有个美味度 $d_i$ ,每升的价格 $p_i$,和最多有 $l_i$ 升。第i个小朋友付的价格不超过 $g_i$ ,但要获得至少 $L_i$ 升的果汁,问美味度最小值的最大值是多少?
$n.m \leq 100000$
sol:
「按题意模拟即可」—— Destinies_Gdx
一眼二分,然后我们考虑暴力,暴力显然就是把美味度小于等于 mid 的果汁价格拿出来排序,选最便宜的
但太慢了,于是我们可以以价格为下标,美味度为权值搞一个主席树,查询类似区间第 k 小
[CTSC2018]混合果汁 [CTSC2018]混合果汁 题目大意: 有n(n≤105) 种果汁,每种果汁有三个属性:美味度di、单价pi和最大体积vi(di,pi,vi≤105)。有m(m≤105)个人来买混合果汁,并且希望在价格不超过gi的情况下买到体积至少为li(gi,li≤109) 的混合果汁。定义一种混合果汁的美味度为构成此种混合果汁的所有果汁中最小的美味度。求每个人能喝到的美味度最大是多少? 思路: 对所有果汁的di 排序。建立主席树维护单价区间能购买到的最大体积之和与总金额之和。对于每个人二分答案k,在主席树上查找美味度不小于k,尽可能选择单价小的果汁最多能购买的混合果汁体积。若体积大于li则说明答案≥k。时间复杂度O(nlog2n) 。 源代码: #include<cstdio> #include<cctype> #include<algorithm> typedef long long int64; inline int64 getint() { register char ch; while(!isdigit(ch=getchar())); register int64 x=ch^'0'; while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); return x; } const int N=1e5+1,logN=18; int n,m,lim,tmp[N]; struct Juice { int d,p,v; bool operator < (const Juice &rhs) const { return d<rhs.d; } }; Juice juice[N]; class FotileTree { private: struct Node { int64 v,s; int left,right; }; Node node[N*logN]; int sz,new_node(const int &q) { node[++sz]=node[q]; return sz; } public: int root[N]; void insert(const int &q,int &p,const int &b,const int &e,const int &g,const int &v) { p=new_node(q); node[p].v+=v; node[p].s+=(int64)g*v; if(b==e) return; const int mid=(b+e)>>1; if(g<=mid) insert(node[q].left,node[p].left,b,mid,g,v); if(g>mid) insert(node[q].right,node[p].right,mid+1,e,g,v); } int64 query(const int &q,const int &p,const int &b,const int &e,const int64 &g) { if(b==e) return std::min(g/b,node[p].v-node[q].v); const int mid=(b+e)>>1; const int64 tmp1=node[node[p].left].s-node[node[q].left].s; const int64 tmp2=node[node[p].left].v-node[node[q].left].v; if(tmp1==g) return tmp2; if(tmp1>g) { return query(node[q].left,node[p].left,b,mid,g); } else { return tmp2+query(node[q].right,node[p].right,mid+1,e,g-tmp1); } } }; FotileTree t; inline bool check(const int &k,const int64 &g,const int64 &v) { return t.query(t.root[tmp[k]-1],t.root[n],1,lim,g)>=v; } int main() { n=getint(),m=getint(); for(register int i=1;i<=n;i++) { const int d=getint(),p=getint(),v=getint(); juice[i]=(Juice){d,p,v}; lim=std::max(lim,p); } std::sort(&juice[1],&juice[n+1]); for(register int i=1;i<=n;i++) { const int &d=juice[i].d,&p=juice[i].p,&v=juice[i].v; if(d!=juice[tmp[tmp[0]]].d) tmp[++tmp[0]]=i; t.insert(t.root[i-1],t.root[i],1,lim,p,v); } for(register int i=0;i<m;i++) { const int64 g=getint(),v=getint(); int l=1,r=tmp[0]; while(l<=r) { const int mid=(l+r)>>1; if(check(mid,g,v)) { l=mid+1; } else { r=mid-1; } } printf("%d\n",check(l-1,g,v)?juice[tmp[l-1]].d:-1); } return 0; }