2019/8/22 校内模拟赛 考试报告
A.地鼠游戏(\(mouse.cpp\))
标准贪心,我的做法是将时间线翻转,这样就变成了"过一会就有一个地鼠冒出来",用优先队列维护已经冒出来的地鼠中分值的最大值.
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int SIZE=100005;
int n,Las=1,Ans;
struct comb
{
int Tim,v;
bool operator <(const comb &x)const
{
return v<x.v;
}
}C[SIZE];
priority_queue<comb>q;
bool cmp(comb A,comb B)
{
return A.Tim<B.Tim;
}
int main()
{
freopen("mouse.in","r",stdin);
freopen("mouse.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&C[i].Tim);
C[i].Tim=50000-C[i].Tim+1;
}
for(int i=1;i<=n;i++)scanf("%d",&C[i].v);
sort(C+1,C+1+n,cmp);
for(int nowT=1;nowT<=50000;nowT++)
{
while(Las<=n&&C[Las].Tim<=nowT)q.push(C[Las++]);
if(q.size()){Ans+=q.top().v;q.pop();}
}
printf("%d",Ans);
return 0;
}
B.[HEOI2015]兔子与樱花(\(sakura.cpp\))
题意:给定一颗有根树,每个节点有一个权值,将删除点\(x\)定义为把\(x\)的权值累加到\(x\)的父亲上,把\(x\)的所有儿子接到\(x\)的父亲上.求满足所有点的点权不超过常数\(m\)时最多可以删除多少个点.
经验贪心.我在考场上的想法是深搜,自底向上更新,能删则删,因为删除一个点带来的最坏影响仅仅是使得它的父节点不能被删,无论怎样答案不会更劣.
但是这个做法还涉及到\(DFS\)顺序的问题,当一个点有多个儿子时,应该优先删除权值小的.这样就要调整\(DFS\)的写法,用优先队列或者其它数据结构来维护儿子中最小的权值.
#include<cstdio>
#include<queue>
using namespace std;
const int SIZE=4000005;
int n,m,head[SIZE],nex[SIZE],ve[SIZE],F[SIZE],C[SIZE],Tot,k,Ans,x;
void Link(int u,int v)
{
nex[++Tot]=head[u];head[u]=Tot;ve[Tot]=v;
nex[++Tot]=head[v];head[v]=Tot;ve[Tot]=u;
}
struct node
{
int v,Cx;
bool operator <(const node &x)const{return Cx>x.Cx;}
};
void DFS(int u)
{
priority_queue<node>q;
while(q.size())q.pop();
for(int i=head[u];i;i=nex[i])
if(ve[i]!=F[u])
{
DFS(ve[i]);
q.push((node){ve[i],C[ve[i]]});
}
while(q.size())
{
if(C[u]+C[q.top().v]-1<=m)
{C[u]+=C[q.top().v]-1;++Ans;q.pop();}
else break;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&C[i]);
for(int i=1;i<=n;i++)
{
scanf("%d",&k);
C[i]+=k;
while(k--){scanf("%d",&x);F[x+1]=i;Link(i,x+1);}
}
DFS(1);
printf("%d\n",Ans);
return 0;
}
C.MET-Meteors(\(meteors.cpp\))
整体二分,二分答案,将答案属于\([L,Mid]\)的国家放入左区间,将答案属于\([Mid+1,R]\)的国家放入右区间,递归处理.
在二分的细节上,直接模拟第\([L,Mid]\)次陨石事件,统计结果时,需要"区间修改,查询部分不连续的单点的和",我差分后直接采用了暴力\(O(m)\)统计的方法,因为我觉得如果用其它数据结构时间复杂度也很高,还不如直接暴力.但我还是太菜了,数据结构虽然看起来复杂度高,但是可以证明复杂度是对的.而本题数据范围较大,极限情况超过\(O(n^2)\)的暴力复杂度无法接受😔.只能采用树状数组维护差分前缀和,区间修改,多次单点查询的方式来达到上述效果.
这也再次警示,在类似的分治算法中,每一次处理区间的复杂度必须只与区间长度相关,否则复杂度就有问题😿.
#include<cstdio>
#define LL long long
const int SIZE=300005;
int n,m,k,o[SIZE],Lx[SIZE],Rx[SIZE],A[SIZE],Ans[SIZE],head[SIZE],nex[SIZE],pos[SIZE],Tot;
void Ins(int u,int v){nex[++Tot]=head[u];head[u]=Tot;pos[Tot]=v;}
struct oo{int P,ID;}x[SIZE],Tem[SIZE];
LL C[SIZE];
void change(int i,int x){for(;i<=m;i+=i&(-i))C[i]+=x;}
LL sum(int i){LL x=0;for(;i;i-=i&(-i))x+=C[i];return x;}
void Do(int H,int T,int L,int R)
{
if(H>T)return;
if(L==R)
{
for(int i=H;i<=T;i++)
Ans[x[i].ID]=L;
return;
}
int nL=H,nR=T,Mid=(L+R)>>1;
for(int i=L;i<=Mid;i++)
{
if(Lx[i]<=Rx[i])
{
change(Lx[i],A[i]);
change(Rx[i]+1,-A[i]);
}
else
{
change(1,A[i]);
change(Rx[i]+1,-A[i]);
change(Lx[i],A[i]);
}
}
for(int i=H;i<=T;i++)
{
LL Gain=0;
for(int k=head[x[i].ID];k;k=nex[k])
{
Gain+=sum(pos[k]);
if(Gain>=x[i].P)break;//防止超过LL
}
if(Gain<x[i].P){x[i].P-=Gain;Tem[nR--]=x[i];}//不够
else Tem[nL++]=x[i];//多了
}
for(int i=L;i<=Mid;i++)
{
if(Lx[i]<=Rx[i])
{
change(Lx[i],-A[i]);
change(Rx[i]+1,A[i]);
}
else
{
change(1,-A[i]);
change(Rx[i]+1,A[i]);
change(Lx[i],-A[i]);
}
}
for(int i=H;i<=T;i++)x[i]=Tem[i];
Do(H,nL-1,L,Mid);
Do(nR+1,T,Mid+1,R);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d",&o[i]);
Ins(o[i],i);
}
for(int i=1;i<=n;i++){scanf("%d",&x[i].P);x[i].ID=i;}
scanf("%d",&k);
for(int i=1;i<=k;i++)scanf("%d%d%d",&Lx[i],&Rx[i],&A[i]);
Do(1,n,1,k+1);
for(int i=1;i<=n;i++)
{
if(Ans[i]==k+1)puts("NIE");
else printf("%d\n",Ans[i]);
}
return 0;
}
附:原来的暴力代码
void Do(int H,int T,int L,int R) { if(H>T)return; if(L==R) { for(int i=H;i<=T;i++) Ans[x[i].ID]=L; return; } int nL=H,nR=T,Mid=(L+R)>>1; LL now=0; for(int i=1;i<=m;i++)D[i]=0; for(int i=L;i<=Mid;i++) { if(Lx[i]<=Rx[i]){D[Lx[i]]+=A[i];D[Rx[i]+1]-=A[i];} else{D[1]+=A[i];D[Rx[i]+1]-=A[i];D[Lx[i]]+=A[i];} } for(int i=1;i<=n;i++)Gain[i]=0; for(int i=1;i<=m;i++){now+=D[i];Gain[o[i]]+=now;} for(int i=H;i<=T;i++) { if(Gain[x[i].ID]<x[i].P){x[i].P-=Gain[x[i].ID];Tem[nR--]=x[i];}//²»¹» else Tem[nL++]=x[i];//¶àÁË } for(int i=H;i<=T;i++)x[i]=Tem[i]; Do(H,nL-1,L,Mid); Do(nR+1,T,Mid+1,R); }