CSP-S模拟4 石子游戏 大鱼吃小鱼 黑客 黑客-续
T2:线段树二分【序列问题】
https://tg.hszxoj.com/contest/527/problem/2
一个显然的贪心是每次吃当前能吃的体积最大的鱼。
考虑直接模拟这个过程,可能会吃N次鱼。我们考虑当前不能吃的体积最小
的鱼,显然只有我们的体积超过了这条鱼,我们的可选集合才会有变化,如果这
条鱼的体积大于等于我们的目标,那我们只需要计算现有的集合需要吃几条能达
到目标即可。于是,我们每一步需要做的就是对于确定的数字集合,求出使得和
达到某个值的最少步数即可,由于可选的数字是连续的,在线段树上二分即可。
接下来我们来分析数字范围变化的次数。考虑我们当前和为A,不能选的最
小体积是B(显然B>A),我们在选出一些数字之后,得到A+x>B,接着我们得到
了选不到的最小体积C,我们再次二分会得到A+x+y>C,注意到,y≥B,也就是
说,A+x+y≥A+x+B>A+B>A+A,于是,我们在两次二分之后,手里的和至少翻
倍,所以我们只需要log次就一定能得到答案
对于线段树在每次询问删除后的恢复,只需要另外开一个数组记录本次删除都涉及了那些节点,最后再恢复就行。因为只涉及我操作的时候的节点,所以复杂度是对的
O(m*log^2n)
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 400000+100
using namespace std;
int n,q,w[N],san[N<<2],tot,ans,zhi,now,ne,tong1[N<<2];
bool bo=1,vis[N<<2],vvv=1;
struct jj
{int opt,s,k,w;}Q[N];
multiset<int>st;
struct tt{int sum,siz,tag;}tmp[N<<3];
struct zxb
{int sum[N<<2],siz[N<<2],tag[N<<2];
inline void pushup(int x)
{ if(!vis[x] and vvv)
{tong1[++zhi]=x,tmp[zhi]=(tt)
{sum[x],siz[x],tag[x]},vis[x]=1;}
sum[x]=sum[x<<1]+sum[x<<1|1];
siz[x]=siz[x<<1]+siz[x<<1|1];
return;
}
inline void pushdown(int x)
{
if(!vis[x<<1])
{tong1[++zhi]=x<<1,
tmp[zhi]=(tt){sum[x<<1],siz[x<<1],tag[x<<1]},vis[x<<1]=1;}
if(!vis[x<<1|1])
{tong1[++zhi]=x<<1|1,
tmp[zhi]=(tt){sum[x<<1|1],
siz[x<<1|1],tag[x<<1|1]},vis[x<<1|1]=1;}
sum[x<<1]=sum[x<<1|1]=siz[x<<1]=siz[x<<1|1]=0;
tag[x<<1]=tag[x<<1|1]=1;tag[x]=0;
}
inline void ins(int x,int l,int r,int pos,int val)
{ if(l==r)
{ siz[x]+=val;
sum[x]+=san[l]*val;
return;
}
int mid=(l+r)>>1;
if(mid<pos)ins(x<<1|1,mid+1,r,pos,val);
else ins(x<<1,l,mid,pos,val);
pushup(x);
}
inline int query(int x,int l,int r,int L,int R)
{ if(L>R)return 0;
if(l>=L and r<=R)return sum[x];
int mid=(l+r)>>1,res=0;
if(tag[x])pushdown(x);
if(mid<R and sum[x<<1|1])res+=query(x<<1|1,mid+1,r,L,R);
if(mid>=L and sum[x<<1])res+=query(x<<1,l,mid,L,R);
return res;
}
inline void calc(int x,int l,int r,int L,int R)
{ if(l==r and sum[x]>=ne)
{ if(!vis[x])
{tong1[++zhi]=x,tmp[zhi]=(tt){sum[x],siz[x],tag[x]},vis[x]=1;}
siz[x]-=(ne/san[l])+(ne%san[l]!=0);
ans+=(ne/san[l])+(ne%san[l]!=0);
now+=((ne/san[l])+(ne%san[l]!=0))*san[l];
ne-=((ne/san[l])+(ne%san[l]!=0))*san[l];
bo=0;
sum[x]=siz[x]*san[l];
return;
}
if(r<=R)
{ if(sum[x]<=ne)
{ ne-=sum[x];now+=sum[x];
if(ne<=0)bo=0;ans+=siz[x];
if(!vis[x])
{tong1[++zhi]=x,tmp[zhi]=(tt){sum[x],siz[x],tag[x]},vis[x]=1;}
sum[x]=0;siz[x]=0;tag[x]=1;
return;
}
}
int mid=(l+r)>>1;
if(tag[x])pushdown(x);
if(mid>=R){(void)calc(x<<1,l,mid,L,R);pushup(x);return ;}
if(bo and sum[x<<1|1])calc(x<<1|1,mid+1,r,L,R);
if(bo and sum[x<<1])calc(x<<1,l,mid,L,R);
pushup(x);
}
}tree;
inline void work(int s,int k)
{
int need=k-s,val;now=s;ans=0;zhi=0;
while(now<k)
{ auto it=st.lower_bound(now);
if(it==st.end())val=999999999999999999;
else val=*it;
ne=min(k-now,val-now+1);int lst=ne;
int pos=lower_bound(san+1,san+1+tot,now)-san;bo=1;
--pos;
if(pos)tree.calc(1,1,tot,1,pos);
else break;
if(bo)break;if(it==st.end())break;
need-=(lst-ne);
}
if(ne>0)puts("-1");
else printf("%lld\n",ans);
for(int i=1;i<=zhi;++i)
{ int x=tong1[i];
vis[x]=0;
tree.sum[x]=tmp[i].sum;
tree.siz[x]=tmp[i].siz;
tree.tag[x]=0;
}
}
signed main()
{ freopen("fish.in","r",stdin);
freopen("fish.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;++i)scanf("%lld",&w[i]),san[++tot]=w[i],st.insert(w[i]);
scanf("%lld",&q);
for(int i=1;i<=q;++i)
{ scanf("%lld",&Q[i].opt);
if(Q[i].opt==1)scanf("%lld%lld",&Q[i].s,&Q[i].k);
if(Q[i].opt==2)scanf("%lld",&Q[i].w),san[++tot]=Q[i].w;
if(Q[i].opt==3)scanf("%lld",&Q[i].w),san[++tot]=Q[i].w;
}
sort(san+1,san+1+tot);
tot=unique(san+1,san+1+tot)-san-1;
san[++tot]=999999999999999999;
for(int i=1;i<=n;++i)
{ int pos=lower_bound(san+1,san+1+tot,w[i])-san;
vvv=0;
tree.ins(1,1,tot,pos,1);
}
vvv=1;
for(int i=1;i<=q;++i)
{ if(Q[i].opt==1)
{ if(Q[i].s>=Q[i].k){puts("0");continue;}
work(Q[i].s,Q[i].k);
}
if(Q[i].opt==2)
{ vvv=0;
int pos=lower_bound(san+1,san+1+tot,Q[i].w)-san;
tree.ins(1,1,tot,pos,1);st.insert(Q[i].w);
vvv=1;
}
if(Q[i].opt==3)
{ vvv=0;
int pos=lower_bound(san+1,san+1+tot,Q[i].w)-san;
tree.ins(1,1,tot,pos,-1);st.erase(st.find(Q[i].w));
vvv=1;
}
}
}
T3:
给你A,B,C,D求x=[A,B],y=[C,D]范围内的数[x/y]最简形式(x'+y')<=999的数的[x'+y']的总计和值.(a,b,c,d<=1e14)
发现合法的(a/b)形式的数很少,所以枚举化简之后是(a/b)形式的数在[A,B][C,D]之间的有多少对,取交集就行。
【易错】freopen("a.in","r",stdin),字字珠玑,不能在文件名后面多空格!!! 比如这样 freopen("a.in ","r",stdin)
,不然你就完了......
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define ull unsigned long long
inline ll re()
{
ll x=0,h=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')h=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*h;
}
const int N=5e5+100;
const ll mod=1e9+7;
ll a,b,c,d;
inline ll gcd(ll x,ll y)
{
if(!y)return x;
return gcd(y,x%y);
}
int main()
{
freopen("hacker.in","r",stdin);
freopen("hacker.out","w",stdout);
a=re(),b=re(),c=re(),d=re();ll sum=0;
for(ll i=1;i<=999;++i)
for(ll j=1;j<=999-i;++j)
{
if(gcd(i,j)!=1)continue;
// chu("i:%lld j:%lld\n",i,j);
ll lb=ceill((double)a/i),rb=b/i;
ll lb2=ceill((double)c/j);
ll rb2=d/j;
lb=max(lb,lb2);
rb=min(rb,rb2);
if(rb<lb)continue;
// if(i==5&&j==3)chu("%d--%d\n",lb,rb);
sum+=(ll)(i+j)*(rb-lb+1);
sum%=mod;
}
chu("%lld",sum);
return 0;
}
/*
*/