QAQ 终于回到衡中了,昨天做了一个晚上的火车,所以没来的及补题解
Em 儿童节快乐,撒花~~
第一题
NOIP难度?提到国王游戏都提醒你怎么做了
直接裸上排序不等式化简一下就可以了
具体化简过程就不再赘述了
#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> using namespace std; typedef long long LL; const int maxn=100010; int T,n; struct OP{ LL a,b; }c[maxn]; LL sum,ans,tmp; bool cmp(const OP &A,const OP &B){ return A.a+B.b+max(A.b,B.a)<B.a+A.b+max(A.a,B.b); } void read(LL &num){ num=0;char ch=getchar(); while(ch<‘!‘)ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘)num=num*10+ch-‘0‘,ch=getchar(); } int main(){ scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n;++i)read(c[i].a),read(c[i].b); sort(c+1,c+n+1,cmp); sum=tmp=ans=0; for(int i=1;i<=n;++i){ sum+=c[i].a; tmp=max(tmp,sum)+c[i].b; }ans=tmp; printf("%lld\n",ans); }return 0; }
第二题
貌似是在湖南雅礼培训的时候考过的原题?
现在看看是二维曼哈顿距离最小生成树的裸题
随便写写之后在树上做一下倍增就可以了
#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #include<cmath> using namespace std; const int maxn=100010; const int oo=0x7fffffff; int n,m,cnt=0; int u,v; struct Edge{ int u,v,w; }c[1000010]; int tmp[maxn]; int fa[maxn]; int anc[maxn][20]; int mx[maxn][20]; int dep[maxn]; int h[maxn],tot=0; struct edge{ int to,next,w; }G[maxn<<1]; struct pos{ int x,y,id; }p[maxn],P[maxn]; struct BIT{ int mn,p; }t[maxn]; int ufs(int x){return fa[x]==x?x:fa[x]=ufs(fa[x]);} bool cmp(const pos &A,const pos &B){ if(A.x==B.x)return A.y<B.y; return A.x<B.x; } bool cmpdis(const Edge &A,const Edge &B){ return A.w<B.w; } void add(int x,int y,int z){ ++tot;G[tot].to=y;G[tot].next=h[x];G[tot].w=z;h[x]=tot; } int sqr(int x){return x*x;} int dis(int i,int j){ return abs(P[i].x-P[j].x)+abs(P[i].y-P[j].y); } int lowbit(int x){return x&(-x);} void UPD(int x,int mn,int id){ for(int i=x;i<=m;i+=lowbit(i)){ if(t[i].mn>mn){ t[i].mn=mn;t[i].p=id; } }return; } int ask(int x){ int mn=oo,p=-1; for(int i=x;i>=1;i-=lowbit(i)){ if(t[i].mn<mn){ mn=t[i].mn;p=t[i].p; } }return p; } void make_Graph(){ for(int flag=0;flag<4;++flag){ if(flag==1||flag==3)for(int i=1;i<=n;++i)swap(p[i].x,p[i].y); else if(flag==2)for(int i=1;i<=n;++i)p[i].y=-p[i].y; for(int i=1;i<=n;++i)tmp[i]=p[i].x-p[i].y; sort(tmp+1,tmp+n+1); m=unique(tmp+1,tmp+n+1)-tmp-1; sort(p+1,p+n+1,cmp); for(int i=1;i<=n;++i)t[i].mn=oo,t[i].p=-1; for(int i=n;i>=1;--i){ int Pos=lower_bound(tmp+1,tmp+m+1,p[i].x-p[i].y)-tmp; int cur=ask(Pos); if(cur!=-1){ ++cnt; c[cnt].u=p[i].id;c[cnt].v=cur; c[cnt].w=dis(c[cnt].u,c[cnt].v); }UPD(Pos,p[i].x+p[i].y,p[i].id); } }return; } void Kruscal(){ sort(c+1,c+cnt+1,cmpdis); for(int i=1;i<=n;++i)fa[i]=i; for(int i=1;i<=cnt;++i){ int d1=ufs(c[i].u),d2=ufs(c[i].v); if(d1!=d2){ fa[d1]=d2; add(c[i].u,c[i].v,c[i].w); add(c[i].v,c[i].u,c[i].w); } }return; } void DFS(int u,int f){ anc[u][0]=f; for(int i=h[u];i;i=G[i].next){ int v=G[i].to; if(v==f)continue; mx[v][0]=G[i].w; dep[v]=dep[u]+1; DFS(v,u); }return; } void pre_LCA(){ for(int i=1;i<=n;++i){ for(int j=1;(1<<j)<=n;++j)anc[i][j]=-1,mx[i][j]=-oo; } for(int j=1;(1<<j)<=n;++j){ for(int i=1;i<=n;++i){ if(anc[i][j-1]!=-1){ int a=anc[i][j-1]; anc[i][j]=anc[a][j-1]; mx[i][j]=max(mx[i][j-1],mx[a][j-1]); } } }return; } int LCA(int p,int q){ if(dep[p]<dep[q])swap(p,q); int log,ans=0; for(log=0;(1<<log)<=dep[p];++log);--log; for(int i=log;i>=0;--i){ if(dep[p]-(1<<i)>=dep[q]){ ans=max(ans,mx[p][i]); p=anc[p][i]; } } if(p==q)return ans; for(int i=log;i>=0;--i){ if(anc[p][i]!=-1&&anc[p][i]!=anc[q][i]){ ans=max(ans,mx[p][i]);p=anc[p][i]; ans=max(ans,mx[q][i]);q=anc[q][i]; } }ans=max(ans,mx[p][0]);ans=max(ans,mx[q][0]); return ans; } int main(){ scanf("%d",&n); for(int i=1;i<=n;++i)scanf("%d%d",&p[i].x,&p[i].y),p[i].id=i,P[i]=p[i]; make_Graph();Kruscal(); DFS(1,-1);pre_LCA(); scanf("%d",&m); while(m--){ scanf("%d%d",&u,&v); if(u==v){printf("0\n");continue;} printf("%d\n",LCA(u,v)); }return 0; }
第三题
第三题失误好大。。一开始看题第一感dp,然后就陷在dp中拔不出来了
这道题目最大的性质就是一个点只能增加不能减少
如果可以减少的话就是只能写架设电话线那种O(n*a)的dp了
我们考虑每个点只能增加,那么一个点当且仅当左右都比他高的时候增加才是有意义的
但是一个点的高度增加之后可能会对其他点是否增加产生影响
不难发现只有高度比这个点小的点增加才会对这个点产生影响
那么我们把点从小到大排序依次增加就可以了
问题就转化成了如何O(1)的计算要增加多少以及代价
然后考虑每一步的收益,拆掉x^2变成每一步是2*base+1(base是之前增高的次数)
用并查集维护联通块内的sigma(base)就可以了,化简一下式子就可以O(1)判断了
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cstdlib> using namespace std; const int maxn=100010; const int oo=0x7fffffff/3; typedef long long LL; int cnt; LL n,c,ans; LL Num,mx,sz; LL a[maxn]; LL sum[maxn]; int pre[maxn],nxt[maxn]; int L[maxn],R[maxn]; int val[maxn]; int fa[maxn]; struct OP{ int v,id; }t[maxn]; bool cmp(const OP &A,const OP &B){return A.v<B.v;} int ufs(int x){return fa[x]==x?x:fa[x]=ufs(fa[x]);} void del(int x){pre[nxt[x]]=pre[x];nxt[pre[x]]=nxt[x];} int main(){ scanf("%lld%lld",&n,&c); for(int i=1;i<=n;++i)scanf("%lld",&a[i]); a[0]=oo;cnt=0; for(int i=1;i<=n;++i){ if(a[i]!=a[i-1]){ ++cnt; pre[cnt]=cnt-1; nxt[cnt]=cnt+1; L[cnt]=i;R[cnt]=i; val[cnt]=a[i]; }else R[cnt]++; }nxt[0]=1;pre[cnt+1]=cnt; for(int i=1;i<=cnt;++i)fa[i]=i,t[i].v=val[i],t[i].id=i; fa[0]=0;fa[cnt+1]=cnt+1; sort(t+1,t+cnt+1,cmp); for(int i=1;i<cnt;++i){ int now=ufs(t[i].id); Num=2;sz=R[now]-L[now]+1;mx=0; int l=ufs(pre[now]),r=ufs(nxt[now]); if(l==0||r==cnt+1){ Num=1; if(l==0)mx=val[r]-val[now]; else mx=val[l]-val[now]; }else mx=min(val[l],val[r])-val[now]; if(mx<=0){continue;} LL tmp=(Num*c-2*sum[now]-sz)/(2*sz); if(2*sum[now]+sz>Num*c)tmp=-1; if(tmp>=mx-1){ ans=ans+mx*mx*sz+2*mx*sum[now]; sum[now]+=mx*sz; val[now]+=mx; if(val[now]==val[l]&&val[now]==val[r]){ sum[now]+=sum[l]+sum[r]; L[now]=L[l];R[now]=R[r]; fa[l]=now;fa[r]=now; del(l);del(r); }else if(val[now]==val[l]){ sum[now]+=sum[l]; L[now]=L[l];fa[l]=now; del(l); }else{ sum[now]+=sum[r]; R[now]=R[r];fa[r]=now; del(r); } }else if(tmp>=0){ ans=ans+(tmp+1)*(tmp+1)*sz+2*(tmp+1)*sum[now]; sum[now]+=(tmp+1)*sz; val[now]+=tmp+1; } } LL la=val[ufs(nxt[0])]; for(int k=ufs(nxt[ufs(nxt[0])]);k&&k!=cnt+1;k=ufs(nxt[k])){ ans=ans+abs(val[k]-la)*c; la=val[k]; } printf("%lld\n",ans); return 0; }
第三题的失误好大的QAQ
主要是没有发现只能增加的性质,也就是这个性质决定了贪心的正确性
但是现在想想,就是考场上我想出贪心也不敢去写
贪心功力太差,正确性的证明能力决定了自己的信心
看来以后要学一学数学了 QAQ 顺便做做贪心题?
时间: 2024-10-25 21:59:24