得分: 100 + 0 + 100 = 200 100+0+100=200 100+0+100=200( T 2 T2 T2悲惨爆 0 0 0)
P . S . P.S. P.S.由于原题是图片,所以我没有上传题目描述,只有数据。
熟悉主席树的人都知道,这是一道主席树查询区间排名的模板题。
L i n k Link Link
主席树 详见博客 可持久化专题(一)——浅谈主席树:可持久化线段树
但是,由于太久没打主席树,我对它有一些生疏了,结果依然用了一个多小时… …
主席树查询区间排名的大致思路,就是将元素给离散化,然后用 n n n棵线段树(合并成一棵主席树)分别存储 1 ∼ i 1\sim i 1∼i中每个元素出现次数,然后就可以很方便地利用前缀和思想查找了。
代码如下:
#include
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define uint unsigned int
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define abs(x) ((x)<0?-(x):(x))
#define INF 1e9
#define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
#define ten(x) (((x)<<3)+((x)<<1))
#define N 100000
#define LogN 40
using namespace std;
int n,cnt,a[N+5],p[(N<<1)+5];
struct Query
{
int l,r,val;
Query(int x=0,int y=0,int z=0):l(x),r(y),val(z){}
}q[N+5];
class FIO
{
private:
#define Fsize 100000
#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
#define pc(ch) (FoutSize
int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
public:
FIO() {FinNow=FinEnd=Fin;}
inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;}
inline void read_char(char &x) {while(isspace(x=tc()));}
inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
inline void write_char(char x) {pc(x);}
inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
inline void end() {fwrite(Fout,1,FoutSize,stdout);}
}F;
class Class_ChairmanTree//主席树
{
private:
#define PushUp(rt) (node[rt].Size=node[node[rt].Son[0]].Size+node[node[rt].Son[1]].Size+1)//上传操作
int n,tot,v,data[N+5],Root[N+5];
struct Tree
{
int Son[2],Size,Cnt;
inline void Clear() {Son[0]=Son[1]=Size=Cnt=0;}
}node[N*LogN+5];
inline void Build(int l,int r,int &rt)//建一棵初始的空树
{
rt=++tot;//新建一个节点,用了动态开点
register int mid=l+r>>1;
if(l^r) Build(l,mid,node[rt].Son[0]),Build(mid+1,r,node[rt].Son[1]);//建树
}
inline void Upt(int l,int r,int lst,int &rt,int pos)//将pos的出现次数加1
{
node[rt=++tot]=node[lst],++node[rt].Size;//新建一个节点,将子树大小加1
if(!(l^r)) return (void)(++node[rt].Cnt);//如果是叶子节点,将出现次数加1
register int mid=l+r>>1;
pos<=mid?Upt(l,mid,node[lst].Son[0],node[rt].Son[0],pos):Upt(mid+1,r,node[lst].Son[1],node[rt].Son[1],pos);//继续操作子树
}
inline int get_rank(int l,int r,int rt,int val)//求出val在[l,r]区间内的排名
{
if(l>r) return 0;//如果l>r,返回0
register int mid=l+r>>1;
if(r<=val) return node[rt].Size;//如果右边界小于等于val,返回这棵子树的大小
if(mid<val) return node[node[rt].Son[0]].Size+get_rank(mid+1,r,node[rt].Son[1],val);//如果左边界小于val,返回左子树的大小加上在右子树中询问的结果
else return get_rank(l,mid,node[rt].Son[0],val);//否则,返回在左子树中询问的结果
}
public:
inline void Clear() {for(register int i=0;i<=tot;++i) node[i].Cnt;tot=v=0;}//清空
inline void Init(int len) {Build(1,n=len,Root[0]);}//建树
inline void Add(int pos) {++v,Upt(1,n,Root[v-1],Root[v],pos);}//将某个元素出现次数加1
inline int GetRank(int l,int r,int val) {return get_rank(1,n,Root[r],val)-get_rank(1,n,Root[l-1],val);}//求区间排名
}ChairmanTree;
inline int get_pos(int val)//求元素离散化后的值
{
register int l=1,r=cnt,mid=l+r>>1;
for(;l<=r;mid=l+r>>1) p[mid]<val?l=mid+1:r=mid-1;
return l;
}
int main()
{
register int i,T,Q,x,y,z;F.read(T);
while(T--)
{
for(ChairmanTree.Clear(),cnt=0,F.read(n),F.read(Q),i=1;i<=n;++i) F.read(a[i]),p[++cnt]=a[i];
for(i=1;i<=Q;++i) F.read(q[i].l),F.read(q[i].r),F.read(q[i].val),p[++cnt]=q[i].val;
for(sort(p+1,p+cnt+1),cnt=unique(p+1,p+cnt+1)-p-1,ChairmanTree.Init(cnt),i=1;i<=n;++i) ChairmanTree.Add(get_pos(a[i]));//建n棵线段树(合并成一棵主席树)
for(i=1;i<=Q;++i) F.write(ChairmanTree.GetRank(q[i].l,q[i].r,get_pos(q[i].val))),F.write_char('\n');//输出答案
}
return F.end(),0;
}
这题其实还是挺简单的,但比赛最后 10 10 10分钟手贱把打了一半的代码给改炸了,最后爆 0 0 0。
首先,我们将题目中给出的敌人分成两类: B i = 0 B_i=0 Bi=0(共 c n t 1 cnt1 cnt1个)和 B > = 0 B_>=0 B>=0(共 c n t 2 cnt2 cnt2个)。
那么最终答案也无非两种情况:
代码如下:
#include
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define uint unsigned int
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define abs(x) ((x)<0?-(x):(x))
#define INF 1e9
#define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
#define ten(x) (((x)<<3)+((x)<<1))
#define N 100000
using namespace std;
int n,m,ans1,ans2,cnt1,cnt2,s1[N+5],s2[N+5];
class FIO
{
private:
#define Fsize 100000
#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
#define pc(ch) (FoutSize
int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
public:
FIO() {FinNow=FinEnd=Fin;}
inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;}
inline void read_char(char &x) {while(isspace(x=tc()));}
inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
inline void write_char(char x) {pc(x);}
inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
inline void end() {fwrite(Fout,1,FoutSize,stdout);}
}F;
inline void Solve(int x,int s,int t)//对最多有x个敌人可杀,剩余的耐久度为s,且已经杀死敌人数为t时的情况进行处理
{
register int i,res=t;
for(i=1;i<=x&&s+s1[i]<=m;++i) s+=s1[i];//尽量多杀
if((res+=min(i-1,x))>ans1) ans1=res,ans2=s;//更新答案
else if(!(ans1^res)&&s<ans2) ans2=s;
}
int main()
{
int i,j,T,x,y,sum;F.read(T);
while(T--)
{
for(F.read(n),F.read(m),sum=ans1=ans2=cnt1=cnt2=0,i=1;i<=n;++i) F.read(x),F.read(y),sum+=y,y?s2[++cnt2]=x:s1[++cnt1]=x;//将敌人分两种类别存储
sort(s1+1,s1+cnt1+1),sort(s2+1,s2+cnt2+1),Solve(cnt1,0,0);//排序,处理全选B[i]=0的敌人的情况
if(cnt2)//如果有B[i]>0的敌人
{
for(i=2;i<=cnt2;++i) s1[cnt1+i-1]=s2[i];//将除第一个以外的B[i]>0的敌人与B[i]=0的敌人存储在一起
if(sort(s1+1,s1+cnt1+cnt2),s2[1]<=m) Solve(cnt1-(sum-cnt2+1),s2[1],sum+1);//排序+求解,个人认为还是比较好理解的
}
F.write(ans1),F.write_char(' '),F.write(ans2),F.write_char('\n');//输出答案
}
return F.end(),0;
}
原题: 【洛谷2519】[HAOI2011] problem a
L i n k Link Link
此题题解 请参考博客 【洛谷2519】[HAOI2011] problem a(动态规划)
注意,原题中输出与此题刚好相反,直接输出 M a x Max Max即可。
代码如下:
#include
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define uint unsigned int
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define abs(x) ((x)<0?-(x):(x))
#define INF 1e9
#define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
#define ten(x) (((x)<<3)+((x)<<1))
#define N 100000
using namespace std;
int n,cnt=0,cnt_=0,a[N+5],f[N+5];
struct Interval//存储区间信息
{
int l,r,v;//l和r表示边界,v表示出现次数
Interval(int x=0,int y=0):l(x),r(y){v=1;}
inline friend bool operator < (Interval x,Interval y) {return x.r^y.r?x.r<y.r:x.l<y.l;}
inline friend bool operator == (Interval x,Interval y) {return !(x.l^y.l||x.r^y.r);}
}s[N+5];
class FIO
{
private:
#define Fsize 100000
#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
#define pc(ch) (FoutSize
int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
public:
FIO() {FinNow=FinEnd=Fin;}
inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;}
inline void read_char(char &x) {while(isspace(x=tc()));}
inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
inline void write_char(char x) {pc(x);}
inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
inline void end() {fwrite(Fout,1,FoutSize,stdout);}
}F;
inline int find(int x,int len)//二分出一个最大的res使a[res]
{
register int l=0,r=len,mid=l+r+1>>1;
for(;l<r;mid=l+r+1>>1) a[mid]<x?l=mid:r=mid-1;
return l;
}
int main()
{
register int i,j,T,x,y,res,Max;F.read(T);
while(T--)
{
for(F.read(n),i=1,cnt=cnt_=Max=0;i<=n;++i) F.read(x),F.read(y),x+y<n&&(s[++cnt_]=Interval(x+1,n-y),0),a[i]=INF;//存储区间,注意将a数组初始化为INF
for(sort(s+1,s+cnt_+1),i=1;i<=cnt_;++i) cnt&&s[cnt]==s[i]?s[cnt].v=min(s[cnt].v+1,s[cnt].r-s[cnt].l+1):s[++cnt]=s[i];//去重
for(i=1;i<=cnt;++i)//枚举区间
{
for(res=find(s[i].l,Max),j=res+1,f[i]=res+s[i].v;j<=f[i];++j) a[j]=min(a[j],s[i].r);//转移+更新a数组
Max=max(Max,f[i]);//更新最大值
}
F.write(Max),F.write_char('\n');//输出答案
}
return F.end(),0;
}