[冬令营模拟]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 = ,cs = ;char ch = getchar();
for(;!isdigit(ch);ch = getchar())if(ch == '-')cs = -cs;
for(;isdigit(ch);ch = getchar())x = * x + ch - '';
return x * cs;
}
void write(LL x)
{
if(x < ) putchar('-'), x = -x;
if(x >= ) write(x / );
putchar('' + x % );
}
const int maxn = , mod = ;
int n,m,k,b[maxn];
int id[maxn];
int dp[maxn][],inv[maxn];
LL cs[maxn],cnt[maxn],f[maxn];
LL qpow(LL x, LL t)
{
LL ret = ;
while(t)
{
if(t & ) ret = ret * x % mod;
x = x * x % mod;
t >>= ;
}
return ret;
}
int main()
{
freopen("faceless.in","r",stdin);
freopen("faceless.out","w",stdout);
n = read();
for(int i = ; i <= n; i++){dp[i][b[i]=read()] = ;inv[i] = qpow(i, mod - );}
m = read();
while(m--)
{
int op = read();
if(!op)
{
int to = read(),u = read(), v = read();
LL rate = (LL) u * qpow(v, mod - ) % mod;
LL nrate = ( - rate + mod) % mod;
for(int i = ; i <= b[to]; i++)
{
if(i > ) dp[to][i] = dp[to][i] * nrate % mod;
if(i < b[to]) dp[to][i] = (dp[to][i] + dp[to][i + ] * rate) % mod;
}
}
else
{
k = read();
for(int i = ; i <= k; i++) id[i] = read();
memset(cs, , sizeof(cs));memset(cnt,,sizeof(cnt));memset(f,,sizeof(f));
cs[] = ;
for(int i = ; i <= k; i++)
for(int j = i; j >= ; j--)
{
if(j)cs[j] = ((( - dp[id[i]][]) * cs[j - ]) + dp[id[i]][] * cs[j]) % mod;
else (cs[j] *= dp[id[i]][]) %= mod;
}
for(int i = ; i <= k; i++)
{
if(dp[id[i]][])
{
int fm = qpow(dp[id[i]][], mod - );
for(int j = ; j < k; j++)
{
if(j)cnt[j] = (cs[j] - (cnt[j - ] * ( - dp[id[i]][]))) % mod * fm % mod;
else cnt[j] = cs[j] * fm % mod;
f[i] += inv[j + ] * cnt[j] % mod;
}
}
else for(int j = ; j < k; j++) f[i] += cs[j + ] * inv[j + ] % mod;
f[i] = ((f[i] %= mod) * ( - dp[id[i]][])) % mod;
f[i] = (f[i] + mod) % mod;
}
for(int i = ; i <= k; i++){write(f[i]);if(i != k)putchar(' ');}
putchar('\n');
}
}
for(int i = ; i <= n; i++)
{
LL sum = ;
for(int j = ; j <= b[i]; j++)
sum += (LL)j * dp[i][j] % mod;
write(sum % mod);
if(i!=n)putchar(' ');
}
cout<<endl;
return ;
}
T1 100分
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 = ,f = ;char ch = getchar();
for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
for(;isdigit(ch);ch = getchar())x = * x + ch - '';
return x * f;
} inline LL LL_read()
{
LL x = ,f = ;char ch = getchar();
for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
for(;isdigit(ch);ch = getchar())x = * x + ch - '';
return x * f;
}
const int maxn = ;
int n;
int lg[maxn + maxn];
struct rua
{
int first[maxn],to[maxn << ],nx[maxn << ],cnt;
LL val[maxn << ];
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][],reh[maxn],bl[maxn];
LL dis[maxn];
inline void dfs(int x,int fa){ reh[++_tim]=x;ind[x]=_tim;dep[x]=dep[fa]+;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=;i<=_tim;i++)st[i][] = reh[i];
lg[] = -;
for(int i=;i<=_tim;i++)lg[i] = lg[i >> ] + ;
for(int i=;i<=;i++)
for(int j=;j+(<<i)<=_tim;j++)
{
if(dep[st[j][i-]]<dep[st[j+(<<(i-))][i-]])
st[j][i]=st[j][i-];
else st[j][i]=st[j+(<<(i-))][i-];
}
}
inline int lca(int x,int y)
{
if(ind[x]>ind[y])swap(x,y);
int k=lg[ind[y]-ind[x]+],ans;
if(dep[st[ind[x]][k]]<dep[st[ind[y]-(<<k)+][k]]) ans=st[ind[x]][k];
else ans=st[ind[y]-(<<k)+][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=;i<=n;i++)
{
int u = read(),v = read(),w = LL_read();
g1.ins(u,v,w);
}g1.dfs(,);g1.makelca();
for(int i=;i<=n;i++)
{
int u = read(),v = read(),w = LL_read();
g2.ins(u,v,w);
}g2.dfs(,);g2.makelca();
LL ans = -1e16;
for(int i=;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);
}
T2 暴力
#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read()
{
int x = ,f = ;char ch = getchar();
for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
for(;isdigit(ch);ch = getchar())x = * x + ch - '';
return x * f;
}
const int maxn = ++;
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 / ];
LL dis[maxn],f[maxn][],fl[],fr[];
int size[maxn],fson[maxn]={maxn},dep[maxn];
LL lp[],rp[];
int root[maxn / ],fa[maxn << ];
LL val[maxn << ];
int ls[maxn << ],rs[maxn << ];
LL id[],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 = ,r = ;
for(auto e : G1[x]) if(e.to != fa)q[++r] = e;
while(l + <= 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,);
}
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 == -)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] = ;
for(auto e : G1[x])
{
if(e.to == fa || e.to == -)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] * );
}
inline int solve(int x,int sig,int d)
{
if(sig == ){dep[x] = d;return x;}
int sx = ,sy = ,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 = -;break;}
}
for(auto &e : G1[sy])
{
if(e.to == sx){e.to = -;break;}
}
fa[ls[o] = solve(sx,sig - size[sy],d + )] = o;
fa[rs[o] = solve(sy,size[sy],d + )] = o;
return o;
}
inline int ins(int x)
{
int u = x,lst = x;
for(int i=dep[x];i>=;i--)
{
id[++top] = fa[x];fl[top] = fr[top] = -(1LL << );
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]]) / - mxd);
ans = max(ans,(fl[y] + fr[x] + val[id[x]]) / - 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=;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=;i<=n;i++)
{
u = read(),v = read(),w = read();
G2[u].push_back(edge(v,w));
G2[v].push_back(edge(u,w));
}build(,);solve(,tot,);dfs(,,);
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
*/
T2 边分治
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 = ,f = ;char ch = getchar();
for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
for(;isdigit(ch);ch = getchar())x = * x + ch - '';
return x * f;
}
const int maxn = ,mod = ,G = ;
int n,l[maxn];
LL poly[maxn];
inline LL skr(LL x,LL t)
{
LL res = ;
while(t)
{
if(t & )res = res * x % mod;
x = x * x % mod;
t = t >> ;
}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[] = ;poly[] = ;
for(int i=;i<n;i++)
{
LL sig = ;
for(int j=;j<=i-;j++)(sig += ((poly[j] * poly[i - j]) % mod) * (j - )) %= mod;
poly[i] = (sig + ((i - ) * poly[i - ]) % mod) % mod;
}
while(T--)
{
LL ans = ,top = ;
for(int i=;i<=n;i++)l[i] = read(),cnt[i] = ;
if(l[n] != n){puts("");continue;}
int NA = ;
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 = ;break;}
st[++top] = i;
}
if(NA){puts("");continue;}
for(int i=;i<=n;i++)(ans *= poly[cnt[i]]) %= mod;
printf("%lld\n",ans);
}
}
T3 80分 暴力卷积
#include<bits/stdc++.h>
#define LL long long
using namespace std;
#define int long long
const int mod = ,G = ,maxn = ;
inline int read()
{
int x = ,f = ;char ch = getchar();
for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
for(;isdigit(ch);ch = getchar())x = * x + ch - '';
return x * f;
}
int n,L;
inline int skr(int x,int k)
{
int re = ;
for (; k; k >>= , x = x * x % mod)
if (k & ) 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=;i<n;i++)R[i] = (R[i >> ] >> ) | ((i & ) << (L - ));}
inline void ntt(int *a,int type,int n)
{
for(int i=;i<n;i++)if(i < R[i])swap(a[i],a[R[i]]);
for(int i=;i<n;i<<=)
{
int wn = skr(G,(mod - ) / (i << ));
if(type == -)wn = skr(wn,mod - );
for(int j=;j<n;j+=(i<<))
{
int w = ;
for(int k=;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 == -)
{
int inv = skr(n,mod - );
for(int i=;i<n;i++)a[i] = 1LL * a[i] * inv % mod;
}
}
inline void mul(int *a,int *b,int n)
{
ntt(a,,n);ntt(b,,n);
for(int i=;i<n;i++)(a[i] *= b[i]) %= mod;
ntt(a,-,n);
}
inline void CDQ_ntt(int *a,int l,int r)
{
if(l == r)
{
(a[l] += (((l - ) * a[l - ]) % mod)) %= mod;
return;
}
int mid = (l + r) >> ;
CDQ_ntt(a,l,mid);int qi = mid - l;
for(int i=;i<=qi;i++)ca[i] = a[i + l];
for(int i=;i<=qi;i++)cb[i] = a[i + l] * (l + i - ) % mod;
int cn = ,cl = ;for(cn=;cn<=(qi<<);cn<<=)cl++;
init_ntt(cn,cl);
fill(ca + qi + , ca + cn , );
fill(cb + qi + , cb + cn , );
//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+);i<=r;i++)a[i] += ca[i - l - l];
if(l == ){CDQ_ntt(a,mid+,r);return;}
int qk = min(l - ,r - l);
for(cn = ,cl = ;cn <= qi + qk;cn <<= )cl++;
init_ntt(cn,cl);
for(int i=;i<=qk;i++)ca[i - ] = a[i];
for(int i=;i<=qi;i++)cb[i] = a[i + l];
fill(ca + qk - , ca + cn , );
fill(cb + qi + , cb + cn , );
mul(ca,cb,cn);
for(int i=mid+;i<=r;i++)(a[i] += (ca[i - l - ] * (i - ))) %= mod;
CDQ_ntt(a,mid+,r);
} int cnt[maxn];
int st[maxn],l[maxn];
signed main()
{
int T = read();n = read();
poly[] = ;poly[] = ;
if(n > )CDQ_ntt(poly,,n - );
int m = n;
while(T--)
{
LL ans = ,top = ;
for(int i=;i<=m;i++)l[i] = read(),cnt[i] = ;
if(l[m] != m){puts("");continue;}
int NA = ;
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 = ;break;}
st[++top] = i;
}
if(NA){puts("");continue;}
for(int i=;i<=m;i++)(ans *= poly[cnt[i]]) %= mod;
printf("%lld\n",ans);
}
}
分治 FFT
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≤)
种果汁,每种果汁有三个属性:美味度di、单价pi和最大体积vi(di,pi,vi≤)。有m(m≤)个人来买混合果汁,并且希望在价格不超过gi的情况下买到体积至少为li(gi,li≤) 的混合果汁。定义一种混合果汁的美味度为构成此种混合果汁的所有果汁中最小的美味度。求每个人能喝到的美味度最大是多少?
思路: 对所有果汁的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^'';
while(isdigit(ch=getchar())) x=(((x<<)+x)<<)+(ch^'');
return x;
}
const int N=1e5+,logN=;
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)>>;
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+,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)>>;
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+,e,g-tmp1);
}
}
};
FotileTree t;
inline bool check(const int &k,const int64 &g,const int64 &v) {
return t.query(t.root[tmp[k]-],t.root[n],,lim,g)>=v;
}
int main() {
n=getint(),m=getint();
for(register int i=;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[],&juice[n+]);
for(register int i=;i<=n;i++) {
const int &d=juice[i].d,&p=juice[i].p,&v=juice[i].v;
if(d!=juice[tmp[tmp[]]].d) tmp[++tmp[]]=i;
t.insert(t.root[i-],t.root[i],,lim,p,v);
}
for(register int i=;i<m;i++) {
const int64 g=getint(),v=getint();
int l=,r=tmp[];
while(l<=r) {
const int mid=(l+r)>>;
if(check(mid,g,v)) {
l=mid+;
} else {
r=mid-;
}
}
printf("%d\n",check(l-,g,v)?juice[tmp[l-]].d:-);
}
return ;
}
[冬令营模拟]GTSG2018的更多相关文章
- [2018冬令营模拟测试赛(二十一)]Problem A: Decalcomania
[2018冬令营模拟测试赛(二十一)]Problem A: Decalcomania 试题描述 输入 见"试题描述" 输出 见"试题描述" 输入示例 见&quo ...
- [冬令营模拟]wzj的题目#1
T1 少膜一个,T3 暴力写挂 强势 rank1 -> rank2 一场比赛两道线段树分治,给力 T1 password 给你 m 个禁止字符串,求长度为 n 的所有字符串中至少包含这些禁止字符 ...
- jzoj5990. 【北大2019冬令营模拟2019.1.6】Bear (状压dp)
题面 题解 我永远讨厌dp.jpg 搞了一个下午优化复杂度最后发现只要有一个小trick就可以A了→_→.全场都插头dp就我一个状压跑得贼慢-- 不难发现我们可以状压,对于每一行,用状态\(S\)表示 ...
- jzoj5991. 【北大2019冬令营模拟2019.1.6】Juice
题面 题解 好迷-- //minamoto #include<bits/stdc++.h> #define R register #define ll long long #define ...
- jzoj5989. 【北大2019冬令营模拟2019.1.6】Forest (set)
题面 题解 为了一点小细节卡了一个下午--我都怕我瞎用set把电脑搞炸-- 观察一次\(1\)操作会造成什么影响,比如说把\(A[i]\)从\(x\)改成\(y\): \(D[x]\)会\(-1\), ...
- jzoj5984. 【北大2019冬令营模拟2019.1.1】仙人掌 (分块)
题面 题解 数据结构做傻了.jpg 考虑每一个节点,它的儿子的取值最多只有\(O(\sqrt {m})\)种,那么可以用一个双向链表维护儿子的所有取值以及该取值的个数,那么对儿子节点修改一个值就是\( ...
- jzoj5983. 【北大2019冬令营模拟2019.1.1】多边形 (组合数学)
这其实是道打表题--你看我代码就知道了-- 咳咳来点严谨证明好了-- 前方高能请注意 首先,正多边形近似于圆,可以看做在圆里内接多边形.圆内接多边形最多只有三个锐角.因为凸多边形的外角和为\(360\ ...
- [JZOJ5977] 【清华2019冬令营模拟12.15】堆
题目 其中n,q≤500000n,q\leq 500000n,q≤500000 题目大意 让你维护一个堆.支持一下操作: 在某个点的下面加上另一个点,然后进行上浮操作. 询问某一点的权值. 思考历程 ...
- JZOJ[5971]【北大2019冬令营模拟12.1】 party(1s,256MB)
题目 题目大意 给你一棵树,在树上的某一些节点上面有人,要用最小的步数和,使得这些人靠在一起.所谓靠在一起,即是任意两个人之间的路径上没有空的节点(也就是连在一起). N≤200N \leq 200N ...
随机推荐
- cocos2d-x 3.0rc1 使用iconv库 解决UTF8乱码问题
多国语言要用到开源字符转换 iconv 先贴出自己的使用代码 你能够做成头文件 #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) #include &qu ...
- 九度OJ 1346:会员积分排序 (排序)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:967 解决:413 题目描述: 元旦佳节快到了,超市A想要给会员一些奖品.但是奖品有限,所以它需要给这些会员做一个排序,然后将名单输出来.排 ...
- 1.Python学习---helloworld
1.首先访问http://www.python.org/download/去下载最新的python版本. 2.安装下载包,一路next. 3.为计算机添加安装目录搭到环境变量,如图把python的安装 ...
- debian切换sh shell到bash shell
1 安装dpkg-reconfigure命令 切换到root账户即可. 2 dpkg-reconfigure dash 选择no
- git clone了整个远程仓库分支
git之远程标签下载(远程分支) 一般我们发布一个新版本到线上服务器时都会在版本库中打一个标签,这样我们可以随时查看这个打标签的版本,就是说标签其实是版本库中一个快照.git的标签与分支类似,区别是分 ...
- java面向对象入门之创建类
/* Name:如何创建类的实例 Power by Stuart Date:2015-4-23*/ //一个bike测试类 public class bikeTest{ //bike 一个变量 Str ...
- spring项目改名后不能启动的原因及解决办法
今日修改了一个spring项目的项目名称,修改后启动项目Debug as->Debug on server,过了很久也没有出现web首页,仔细看项目的定时器已经启动,eclipse的Consol ...
- Linux 3 -grep
七. grep家族: 1. grep退出状态: 0: 表示成功: 1: 表示在所提供的文件无法找到匹配的pattern: 2: 表示参数中提供的文件不存在. 见如下示例: /> grep 'ro ...
- jQuery源码分析_工具方法(学习笔记)
expando:生成唯一JQ字符串(内部使用) noConflict():防止冲突 isReady:DOM是否加载完成(内部) readyWait:等待多少文件的计数器(内部) holdReady() ...
- Shiro:学习笔记(1)——身份验证
Shiro——学习笔记(1) 1.核心概念 1.Shiro不会自己去维护用户.维护权限:这些需要我们自己去设计/提供:然后通过相应的接口注入给Shiro.2.应用代码直接交互的对象是Subject,也 ...