20181022 考试记录&高级数据结构
T1:
其实是一道easy题,$O(n^3log n)$ 也是能卡过去的,本着要的70分的心态,最后尽然A了。
如果是正解则是$O(n^3)$,当确定你要选择的列时,不断往下扩展,因为此刻是单调函数,所以可以用单调队列优化。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } int n,a[502][502],s[502][502],hs[502][502],k,maxn; bool check(int x1,int y1,int x2,int y2){return hs[x2][y2]-hs[x2][y1-1]-hs[x1-1][y2]+hs[x1-1][y1-1]<=k;} int main() { n=read(),k=read(); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) a[i][j]=read(),s[i][j]=s[i][j-1]+a[i][j]; } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) hs[i][j]=hs[i-1][j]+s[i][j]; } for(int y1=1;y1<=n;y1++){ for(int y2=y1;y2<=n;y2++){ int l=1; for(int r=1;r<=n;r++){ while(!check(l,y1,r,y2)) l++; maxn=max(maxn,(r-l+1)*(y2-y1+1)); } } } printf("%d\n",maxn); return 0; }
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } int n,a[502][502],s[502][502],hs[502][502],k,maxn; bool check(int x1,int y1,int x2,int y2){ int s1=0,s2=0,s3=0; s1=s[x1][y2]-s[x1][y1-1]; if(x1!=x2)s2=s[x2][y2]-s[x2][y1-1]; if(x2>x1+1) s3=(hs[x2-1][y2]-hs[x1][y2])-(hs[x2-1][y1-1]-hs[x1][y1-1]); if(s1+s2+s3>k) return 0; return 1; } int towfen(int x1,int y1,int x2,int ll,int rr){ int l=ll,r=rr,mid,maxn=0; while(l<=r){ mid=l+r>>1; if(check(x1,y1,x2,mid)) maxn=max(maxn,mid),l=mid+1; else r=mid-1; }return maxn; } int main() { freopen("game.in","r",stdin); freopen("game.out","w",stdout); n=read(),k=read(); if(n*n<=k){ cout<<n*n; return 0; } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) a[i][j]=read(),s[i][j]=s[i][j-1]+a[i][j]; } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) hs[i][j]=hs[i-1][j]+s[i][j]; } for(int x1=1;x1<=n;x1++) { for(int y1=1;y1<=n;y1++) { for(int x2=n;x2>=x1;x2--) { if((n-y1+1)*(x2-x1+1)<=maxn) break; int y2=towfen(x1,y1,x2,y1,n); maxn=max(maxn,(x2-x1+1)*(y2-y1+1)); } } } printf("%d",maxn); return 0; } /* 5 4 1 0 1 0 1 0 1 0 0 0 1 0 1 0 0 1 1 1 1 1 0 0 1 0 1 */
T2:
考试时的想法:看到题,发现与YBT的一本通上的一道题十分类似(this),所以就一直在想最小生成树,就再打,打了半天,发现这个方法正确性不显然,打完了,与自己的暴力程序对拍,WAWAWA,全是WA,心态炸了,最后交了个暴力程序,其实树形dp也是想过,但没有想过这么强大的后效性怎么处理掉,所以就放弃了。
$30points$:暴力
$extra 40 points$:
dp方程,设$dp(i)$表示前$i$ 个城市的代价均已计算,且最后一个机场设在了第$i$个城市的最小花费,所以就可以瞎搞了?为什么这样无后效性呢,是因为$dp$方程设计时是算出前面的,为什么与后面的无影响呢,因为可以传参
$100 points$:
树形$dp$.一个现在很明显的东西,所以要设计一个无后效性,快速的方程,然后其实就跟上面的一样了,详情看W神爷的题解
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; inline int read() { int f=1,ans=0;char c; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } struct node{ int u,v,w,nex; }x[5002]; int g[5002],cnt,dis[5002][5002],n,cost[5002],head[5002],dp[5002][5002]; void add(int u,int v,int w){ x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],x[cnt].w=w,head[u]=cnt++; } void dfs(int f,int fa,int w,int root){ dis[root][f]=w; for(int i=head[f];i!=-1;i=x[i].nex){ if(x[i].v==fa) continue; dfs(x[i].v,f,w+x[i].w,root); } } void dp_tree(int f,int fa){ for(int i=head[f];i!=-1;i=x[i].nex){ if(x[i].v==fa) continue; dp_tree(x[i].v,f); } for(int i=1;i<=n;i++){ dp[f][i]=cost[i]+dis[f][i]; for(int j=head[f];j!=-1;j=x[j].nex){ if(x[j].v==fa) continue; dp[f][i]+=min(g[x[j].v],dp[x[j].v][i]-cost[i]); } } for(int i=1;i<=n;i++) g[f]=min(g[f],dp[f][i]); return; } int main() { // freopen("design.in","r",stdin); // freopen("design.out","w",stdout); memset(head,-1,sizeof(head)); memset(g,127/3,sizeof(g)); n=read(); for(int i=1;i<=n;i++) cost[i]=read(); for(int i=1;i<n;i++){int u=read(),v=read(),w=read();add(u,v,w),add(v,u,w);} for(int i=1;i<=n;i++) dfs(i,0,0,i); dp_tree(1,0); cout<<g[1]; } /* 6 2 1 2 4 2 3 1 2 4 1 3 4 2 6 2 2 4 100 4 5 2 */
T3:
线段树优化建图,以前不知道的东西,其实也是一个比较好想的方法,就是建一棵以$n$作为长度的线段树,用$dfs$的时间戳进行编号,方便建图。先将父亲节点向两个儿子连一条长度为$0$的边,然后再每个点向线段树中所对应的点连一条边权为$0$的边。因为每个点有一个移动距离所以可以二分求出它可以到达的左右端点,然后再跑$dijkstra$即可($spfa$,它死了)。为什么这样写是对的呢,为什么要建边权$0$边呢,是因为要保持可连通关系,就像能量流动一样,必须有层传导关系,给下面进行贡献,为什么不向上连边,是因为上面包含的区间比下面的长,就这么简单。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cmath> using namespace std; inline long long read() { long long f=1,ans=0;char c; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } struct node{ long long u,v,w,nex; }x[9922502]; long long head[9922502],dis[9922502],vis[9922502],pos[9922502],w[9922502],t[9922502],n,ll[9922502],rr[9922502],cnt,s1,s2; void add(long long u,long long v,long long w){ x[cnt].u=u,x[cnt].v=v,x[cnt].w=w,x[cnt].nex=head[u],head[u]=cnt++; } long long query_l(long long dis,long long p){ long long l=1,r=n,mid,minn=2<<30-1,pp=dis-p; while(l<=r){ mid=l+r>>1; if(pp<=pos[mid]) minn=min(minn,mid),r=mid-1; else l=mid+1; } return minn; } long long query_r(long long dis,long long p){ long long l=1,r=n,maxn=0,mid,pp=dis+p; while(l<=r){ mid=l+r>>1; if(pp>=pos[mid]) maxn=max(maxn,mid),l=mid+1; else r=mid-1; } return maxn; } void build(long long k,long long l,long long r){ if(l==r){add(k+n,l,0);return;} long long mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); add(k+n,(k<<1)+n,0),add(k+n,(k<<1|1)+n,0); return; } void query(long long k,long long l,long long r,long long x,long long y,long long w,long long st){ if(x<=l&&r<=y){add(st,k+n,w);return;} long long mid=l+r>>1; if(x<=mid) query(k<<1,l,mid,x,y,w,st); if(mid<y) query(k<<1|1,mid+1,r,x,y,w,st); return; } void dijkstra(){ memset(dis,127,sizeof(dis)); priority_queue<pair<long long,long long> > que; dis[s1]=0; que.push(make_pair(0,s1)); while(!que.empty()){ long long xx=que.top().second;que.pop(); if(vis[xx]) continue; vis[xx]=1; for(long long i=head[xx];i!=-1;i=x[i].nex){ if(dis[x[i].v]>dis[xx]+x[i].w){ dis[x[i].v]=dis[xx]+x[i].w; que.push(make_pair(-dis[x[i].v],x[i].v)); } } } return; } int main() { memset(head,-1,sizeof(head)); n=read(),s1=read(),s2=read(); for(long long i=1;i<=n;i++) pos[i]=read(); for(long long i=1;i<=n;i++) w[i]=read(); for(long long i=1;i<=n;i++) t[i]=read(); for(long long i=1;i<=n;i++) ll[i]=query_l(pos[i],w[i]),rr[i]=query_r(pos[i],w[i]); build(1,1,n); for(long long i=1;i<=n;i++) query(1,1,n,ll[i],rr[i],t[i],i); dijkstra(); cout<<dis[s2]; }
分数:$100+30+60=190$