- 上午
- 入门OJ
- 入门OJ 2043: [Noip模拟题]小Y的炮
继续昨晚上的绝望……
实在不行了,学习了一个网上的AC代码,用map实现的,总算是AC了
代码:
#include<map> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; struct cannon{ ll a,d; bool operator <(const cannon &b)const{ return (a<b.a || (a==b.a && d<b.d)); } }c[250005]; ll mountain[250005],last,k; map<ll,ll>f; int n,m,ans,cnt; int main(){ freopen("in.in","r",stdin); freopen("mine.out","w",stdout); scanf("%d%d%lld",&n,&m,&k); for(int i=1;i<=n;i++) scanf("%lld",&mountain[i]); for(int i=1;i<=m;i++) scanf("%lld%lld",&c[i].a,&c[i].d); sort(c+1,c+m+1); for(int i=1;i<=m;i++){ while(cnt&&c[cnt].d<=c[i].d) cnt--; c[++cnt]=c[i]; } last=0;f[0]=0; for(int i=1;i<=cnt;i++){ if(i!=1) last=c[i-1].a; //p[i].d>p[i+1].d for(ll j=max(last,c[i].a-c[i].d)+1;j<=c[i].a;j++){ ll t=(j-last-1)/c[i].d+1; f[j]=f[max(0ll,j-(c[i].d*t))]+t; } } last=0; int p=0; for(int i=n;i>=1;i--){ while(p<=cnt&&c[p].a<mountain[i]) p++; if(p>cnt)break; if(p!=1)last=c[p-1].a; ll t=(mountain[i]-last-1)/c[p].d+1; ll q=f[max(0ll,mountain[i]-(c[p].d*t))]+t; if(k>=q)k-=q,++ans;else break; } printf("%d %lld ",ans,k); return 0; }
- 入门OJ 2043: [Noip模拟题]小Y的炮
- 入门OJ
- 入门OJ 2044: [Noip模拟题]tree
由于边都为正权,即一个节点往上走,累积的边权和只会越来越大。
所以对于每个节点,向上最多只会存在一个祖先到它的边权和为k
那么只需dfs遍历时维护到该点的路径,并二分查找是否存在一个祖先到它的距离为k。
代码:
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 200005 using namespace std; struct edge{ int to,val,next; }e[MAXN*2]; int head[MAXN],sum[MAXN]; int n,rt,k,cnt,ans,ent=1; void add(int u,int v,int w){ e[ent]=(edge){v,w,head[u]}; head[u]=ent++; } void binary_search(){ int tmp=sum[cnt]-k; int sub=lower_bound(sum+1,sum+cnt+1,tmp)-sum; if(sum[sub]==tmp) ans++; } void dfs(int u,int w,int fa){ cnt++; sum[cnt]=sum[cnt-1]+w; binary_search(); for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v==fa) continue; dfs(v,e[i].val,u); } cnt--; } int main(){ scanf("%d%d%d",&n,&rt,&k); for(int i=1,a,b,c;i<n;i++){ scanf("%d%d%d",&a,&b,&c); add(a,b,c); add(b,a,c); } dfs(rt,0,0); printf("%d",ans); return 0; }
- 入门OJ 2047: [Noip模拟题]博览购票
水水题,就如题目下面说的那样:队列入门
代码:
#include<queue> #include<cstdio> #include<cstring> #include<iostream> using namespace std; queue<int>q; int cnt[2005]; int n,m,ans=0x3f3f3f3f,g,a,b; int main(){ scanf("%d%d",&n,&m); for(int i=1,x;i<=n;i++){ scanf("%d",&x); if(!cnt[x]) g++; q.push(x); cnt[x]++; while(cnt[q.front()]>1) cnt[q.front()]--,q.pop(); if(g==m&&q.size()<ans){ ans=q.size(); a=i-q.size()+1; b=i; } } printf("%d %d",a,b); return 0; }
- 沐然选讲生成树题目
- 放弃上午的入门OJ 2048: [Noip模拟题]国家宝藏:限制1MB内存,Floodfill计算连通块……题解有一句话没看懂……;
- 晚上
- 一道恶心但好的入门OJ题
- 入门OJ 2050: [Noip模拟题]钻石游戏
1).预处理和维护
处理出每个位置的Up,Down,Left,Right值,分别表示从该位置出发向四个方向走,有多少个连续相同的数。可以O(n2)预处理。
对于每个操作,首先交换两个位置的数,再对这两个位置影响的行列中的每个元素重新计算一次Up,Down,Left,Right这四个值中需要更改的某些值,即维护出新图的全部的,正确的四个值。维护的时间复杂度O(n)
2).转化问题
由于交换的两个数值不同,所以答案必然在两个位置的两侧。
那么怎么得出最大值呢?
来一张图转化一下问题。
- 入门OJ 2050: [Noip模拟题]钻石游戏
- 一道恶心但好的入门OJ题
3).求解最大值
那么问题转化以后,要求最优解,即可用单调队列维护:
思路:因为一定要包含红色数,所以序列可以按那个位置的最大贡献进行“升级”,形成一个单峰序列。
对于上图中的序列,即得到2 4 5 3 3 3 2
什么意思呢?比如说倒数第二个数从5变为了3,即表示如果区间包含倒数第二个位置,那么一定包含了从三号位置到倒数第二个位置中间的所有数,(在原题中即为这段区间形成的矩形的最大高度,当然不超过该区间的最小值),所以倒数第二个位置维护的即是那段区间的最小值。
概括的说,新序列维护的是原序列每个位置到红色数位置这段区间的最小值。
然后嘛,在红色数的两侧都形成了一个单调队列。那么,只需分别在两边从大到小枚举各元素,以其作为最小元素,在另一侧找到第一个值大于它的位置,那么这段区间即为该最小元素所能贡献的最大值。
由于队列单调,可以做到O(n)
综上,复杂度为 O(n2+mn),但代码很烦……
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 505 using namespace std; int mp[MAXN][MAXN]; int Up[MAXN][MAXN],Down[MAXN][MAXN],Left[MAXN][MAXN],Right[MAXN][MAXN]; int n,m; void calculation_row(int i){ for(int j=1;j<=m;j++) if(j==1||mp[i][j-1]!=mp[i][j]) Left[i][j]=1; else Left[i][j]=Left[i][j-1]+1; for(int j=m;j>=1;j--) if(j==m||mp[i][j+1]!=mp[i][j]) Right[i][j]=1; else Right[i][j]=Right[i][j+1]+1; } void calculation_column(int j){ for(int i=1;i<=n;i++) if(i==1||mp[i-1][j]!=mp[i][j]) Up[i][j]=1; else Up[i][j]=Up[i-1][j]+1; for(int i=n;i>=1;i--) if(i==n||mp[i+1][j]!=mp[i][j]) Down[i][j]=1; else Down[i][j]=Down[i+1][j]+1; } void readin(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&mp[i][j]); for(int i=1;i<=n;i++) calculation_row(i); for(int j=1;j<=m;j++) calculation_column(j); } int get_lr(int x,int y,int t[MAXN][MAXN]){ static int h[MAXN],v[MAXN],cnt,p,ans,now; ans=t[x][y]; cnt=0; p=1; now=t[x][y]; h[++cnt]=t[x][y]; v[cnt]=1; for(int i=x-1;i>=x-Up[x][y]+1;i--){ if(t[i][y]<h[cnt]) h[++cnt]=t[i][y]; v[cnt]=x-i+1; } for(int i=x+1;i<=x+Down[x][y]-1;i++){ now=min(now,t[i][y]); while(p+1<=cnt&&h[p+1]>=now) p++; ans=max(ans,(i-x+v[p])*now); } cnt=0; p=1; now=t[x][y]; h[++cnt]=t[x][y]; v[cnt]=1; for(int i=x+1;i<=x+Down[x][y]-1;i++){ if(t[i][y]<h[cnt]) h[++cnt]=t[i][y]; v[cnt]=i-x+1; } for(int i=x-1;i>=x-Up[x][y]+1;i--){ now=min(now,t[i][y]); while(p+1<=cnt&&h[p+1]>=now) p++; ans=max(ans,(x-i+v[p])*now); } return ans; } int get_ud(int x,int y,int t[MAXN][MAXN]){ static int h[MAXN],v[MAXN],cnt,p,ans,now; ans=t[x][y]; cnt=0; p=1; now=t[x][y]; h[++cnt]=t[x][y]; v[cnt]=1; for(int j=y-1;j>=y-Left[x][y]+1;j--){ if(t[x][j]<h[cnt]) h[++cnt]=t[x][j]; v[cnt]=y-j+1; } for(int j=y+1;j<=y+Right[x][y]-1;j++){ now=min(now,t[x][j]); while(p+1<=cnt&&h[p+1]>=now) p++; ans=max(ans,(j-y+v[p])*now); } cnt=0; p=1; now=t[x][y]; h[++cnt]=t[x][y]; v[cnt]=1; for(int j=y+1;j<=y+Right[x][y]-1;j++){ if(t[x][j]<h[cnt]) h[++cnt]=t[x][j]; v[cnt]=j-y+1; } for(int j=y-1;j>=y-Left[x][y]+1;j--){ now=min(now,t[x][j]); while(p+1<=cnt&&h[p+1]>=now) p++; ans=max(ans,(y-j+v[p])*now); } return ans; } void printmap(){ for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++) printf("%d ",mp[i][j]); printf(" "); } } void work(){ int k,x1,x2,y1,y2,ans; scanf("%d",&k); while(k--){ ans=0; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); swap(mp[x1][y1],mp[x2][y2]); calculation_row(x1); calculation_column(y1); if(x1==x2){ calculation_column(y2); if(y1>y2) swap(y1,y2); ans=max(ans,get_lr(x1,y1,Left)); ans=max(ans,get_lr(x2,y2,Right)); } else{ calculation_row(x2); if(x1>x2) swap(x1,x2); ans=max(ans,get_ud(x1,y1,Up)); ans=max(ans,get_ud(x2,y2,Down)); } printf("%d ",ans); //printmap(); } } int main(){ freopen("in.in","r",stdin); freopen("mine.out","w",stdout); readin(); work(); return 0; }
……………………………………………………………………………………………………
但无奈在入门OJ上一提交就Runtime Error,然后在网上找了几个AC代码,一提交也都是Runtime Error,可能是OJ上的数据有问题。
为了保证我的代码的正确性,写了一个对拍,和网上的AC代码对拍了一晚上,(真的是一晚上,回寝室时忘记关电脑了,一直在对拍o_o),第二天早上来看时仍没又发现错误,那就当是AC了吧,嘿嘿。
- End:
- 一天又过去了,感觉下午效率低下呢,除了听了听讲题,就没做什么了。
- 明天要考试,DP专场,有点方,希望弱弱的我不会爆零。
- 上午
- 入门OJ
- 入门OJ 2043: [Noip模拟题]小Y的炮
继续昨晚上的绝望……
实在不行了,学习了一个网上的AC代码,用map实现的,总算是AC了
代码:
#include<map> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; struct cannon{ ll a,d; bool operator <(const cannon &b)const{ return (a<b.a || (a==b.a && d<b.d)); } }c[250005]; ll mountain[250005],last,k; map<ll,ll>f; int n,m,ans,cnt; int main(){ freopen("in.in","r",stdin); freopen("mine.out","w",stdout); scanf("%d%d%lld",&n,&m,&k); for(int i=1;i<=n;i++) scanf("%lld",&mountain[i]); for(int i=1;i<=m;i++) scanf("%lld%lld",&c[i].a,&c[i].d); sort(c+1,c+m+1); for(int i=1;i<=m;i++){ while(cnt&&c[cnt].d<=c[i].d) cnt--; c[++cnt]=c[i]; } last=0;f[0]=0; for(int i=1;i<=cnt;i++){ if(i!=1) last=c[i-1].a; //p[i].d>p[i+1].d for(ll j=max(last,c[i].a-c[i].d)+1;j<=c[i].a;j++){ ll t=(j-last-1)/c[i].d+1; f[j]=f[max(0ll,j-(c[i].d*t))]+t; } } last=0; int p=0; for(int i=n;i>=1;i--){ while(p<=cnt&&c[p].a<mountain[i]) p++; if(p>cnt)break; if(p!=1)last=c[p-1].a; ll t=(mountain[i]-last-1)/c[p].d+1; ll q=f[max(0ll,mountain[i]-(c[p].d*t))]+t; if(k>=q)k-=q,++ans;else break; } printf("%d %lld ",ans,k); return 0; }
- 入门OJ 2043: [Noip模拟题]小Y的炮
- 入门OJ
- 入门OJ 2044: [Noip模拟题]tree
由于边都为正权,即一个节点往上走,累积的边权和只会越来越大。
所以对于每个节点,向上最多只会存在一个祖先到它的边权和为k
那么只需dfs遍历时维护到该点的路径,并二分查找是否存在一个祖先到它的距离为k。
代码:
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 200005 using namespace std; struct edge{ int to,val,next; }e[MAXN*2]; int head[MAXN],sum[MAXN]; int n,rt,k,cnt,ans,ent=1; void add(int u,int v,int w){ e[ent]=(edge){v,w,head[u]}; head[u]=ent++; } void binary_search(){ int tmp=sum[cnt]-k; int sub=lower_bound(sum+1,sum+cnt+1,tmp)-sum; if(sum[sub]==tmp) ans++; } void dfs(int u,int w,int fa){ cnt++; sum[cnt]=sum[cnt-1]+w; binary_search(); for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v==fa) continue; dfs(v,e[i].val,u); } cnt--; } int main(){ scanf("%d%d%d",&n,&rt,&k); for(int i=1,a,b,c;i<n;i++){ scanf("%d%d%d",&a,&b,&c); add(a,b,c); add(b,a,c); } dfs(rt,0,0); printf("%d",ans); return 0; }
- 入门OJ 2047: [Noip模拟题]博览购票
水水题,就如题目下面说的那样:队列入门
代码:
#include<queue> #include<cstdio> #include<cstring> #include<iostream> using namespace std; queue<int>q; int cnt[2005]; int n,m,ans=0x3f3f3f3f,g,a,b; int main(){ scanf("%d%d",&n,&m); for(int i=1,x;i<=n;i++){ scanf("%d",&x); if(!cnt[x]) g++; q.push(x); cnt[x]++; while(cnt[q.front()]>1) cnt[q.front()]--,q.pop(); if(g==m&&q.size()<ans){ ans=q.size(); a=i-q.size()+1; b=i; } } printf("%d %d",a,b); return 0; }
- 沐然选讲生成树题目
- 放弃上午的入门OJ 2048: [Noip模拟题]国家宝藏:限制1MB内存,Floodfill计算连通块……题解有一句话没看懂……;
- 晚上
- 一道恶心但好的入门OJ题
- 入门OJ 2050: [Noip模拟题]钻石游戏
1).预处理和维护
处理出每个位置的Up,Down,Left,Right值,分别表示从该位置出发向四个方向走,有多少个连续相同的数。可以O(n2)预处理。
对于每个操作,首先交换两个位置的数,再对这两个位置影响的行列中的每个元素重新计算一次Up,Down,Left,Right这四个值中需要更改的某些值,即维护出新图的全部的,正确的四个值。维护的时间复杂度O(n)
2).转化问题
由于交换的两个数值不同,所以答案必然在两个位置的两侧。
那么怎么得出最大值呢?
来一张图转化一下问题。
- 入门OJ 2050: [Noip模拟题]钻石游戏
- 一道恶心但好的入门OJ题
3).求解最大值
那么问题转化以后,要求最优解,即可用单调队列维护:
思路:因为一定要包含红色数,所以序列可以按那个位置的最大贡献进行“升级”,形成一个单峰序列。
对于上图中的序列,即得到2 4 5 3 3 3 2
什么意思呢?比如说倒数第二个数从5变为了3,即表示如果区间包含倒数第二个位置,那么一定包含了从三号位置到倒数第二个位置中间的所有数,(在原题中即为这段区间形成的矩形的最大高度,当然不超过该区间的最小值),所以倒数第二个位置维护的即是那段区间的最小值。
概括的说,新序列维护的是原序列每个位置到红色数位置这段区间的最小值。
然后嘛,在红色数的两侧都形成了一个单调队列。那么,只需分别在两边从大到小枚举各元素,以其作为最小元素,在另一侧找到第一个值大于它的位置,那么这段区间即为该最小元素所能贡献的最大值。
由于队列单调,可以做到O(n)
综上,复杂度为 O(n2+mn),但代码很烦……
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 505 using namespace std; int mp[MAXN][MAXN]; int Up[MAXN][MAXN],Down[MAXN][MAXN],Left[MAXN][MAXN],Right[MAXN][MAXN]; int n,m; void calculation_row(int i){ for(int j=1;j<=m;j++) if(j==1||mp[i][j-1]!=mp[i][j]) Left[i][j]=1; else Left[i][j]=Left[i][j-1]+1; for(int j=m;j>=1;j--) if(j==m||mp[i][j+1]!=mp[i][j]) Right[i][j]=1; else Right[i][j]=Right[i][j+1]+1; } void calculation_column(int j){ for(int i=1;i<=n;i++) if(i==1||mp[i-1][j]!=mp[i][j]) Up[i][j]=1; else Up[i][j]=Up[i-1][j]+1; for(int i=n;i>=1;i--) if(i==n||mp[i+1][j]!=mp[i][j]) Down[i][j]=1; else Down[i][j]=Down[i+1][j]+1; } void readin(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&mp[i][j]); for(int i=1;i<=n;i++) calculation_row(i); for(int j=1;j<=m;j++) calculation_column(j); } int get_lr(int x,int y,int t[MAXN][MAXN]){ static int h[MAXN],v[MAXN],cnt,p,ans,now; ans=t[x][y]; cnt=0; p=1; now=t[x][y]; h[++cnt]=t[x][y]; v[cnt]=1; for(int i=x-1;i>=x-Up[x][y]+1;i--){ if(t[i][y]<h[cnt]) h[++cnt]=t[i][y]; v[cnt]=x-i+1; } for(int i=x+1;i<=x+Down[x][y]-1;i++){ now=min(now,t[i][y]); while(p+1<=cnt&&h[p+1]>=now) p++; ans=max(ans,(i-x+v[p])*now); } cnt=0; p=1; now=t[x][y]; h[++cnt]=t[x][y]; v[cnt]=1; for(int i=x+1;i<=x+Down[x][y]-1;i++){ if(t[i][y]<h[cnt]) h[++cnt]=t[i][y]; v[cnt]=i-x+1; } for(int i=x-1;i>=x-Up[x][y]+1;i--){ now=min(now,t[i][y]); while(p+1<=cnt&&h[p+1]>=now) p++; ans=max(ans,(x-i+v[p])*now); } return ans; } int get_ud(int x,int y,int t[MAXN][MAXN]){ static int h[MAXN],v[MAXN],cnt,p,ans,now; ans=t[x][y]; cnt=0; p=1; now=t[x][y]; h[++cnt]=t[x][y]; v[cnt]=1; for(int j=y-1;j>=y-Left[x][y]+1;j--){ if(t[x][j]<h[cnt]) h[++cnt]=t[x][j]; v[cnt]=y-j+1; } for(int j=y+1;j<=y+Right[x][y]-1;j++){ now=min(now,t[x][j]); while(p+1<=cnt&&h[p+1]>=now) p++; ans=max(ans,(j-y+v[p])*now); } cnt=0; p=1; now=t[x][y]; h[++cnt]=t[x][y]; v[cnt]=1; for(int j=y+1;j<=y+Right[x][y]-1;j++){ if(t[x][j]<h[cnt]) h[++cnt]=t[x][j]; v[cnt]=j-y+1; } for(int j=y-1;j>=y-Left[x][y]+1;j--){ now=min(now,t[x][j]); while(p+1<=cnt&&h[p+1]>=now) p++; ans=max(ans,(y-j+v[p])*now); } return ans; } void printmap(){ for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++) printf("%d ",mp[i][j]); printf(" "); } } void work(){ int k,x1,x2,y1,y2,ans; scanf("%d",&k); while(k--){ ans=0; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); swap(mp[x1][y1],mp[x2][y2]); calculation_row(x1); calculation_column(y1); if(x1==x2){ calculation_column(y2); if(y1>y2) swap(y1,y2); ans=max(ans,get_lr(x1,y1,Left)); ans=max(ans,get_lr(x2,y2,Right)); } else{ calculation_row(x2); if(x1>x2) swap(x1,x2); ans=max(ans,get_ud(x1,y1,Up)); ans=max(ans,get_ud(x2,y2,Down)); } printf("%d ",ans); //printmap(); } } int main(){ freopen("in.in","r",stdin); freopen("mine.out","w",stdout); readin(); work(); return 0; }
……………………………………………………………………………………………………
但无奈在入门OJ上一提交就Runtime Error,然后在网上找了几个AC代码,一提交也都是Runtime Error,可能是OJ上的数据有问题。
为了保证我的代码的正确性,写了一个对拍,和网上的AC代码对拍了一晚上,(真的是一晚上,回寝室时忘记关电脑了,一直在对拍o_o),第二天早上来看时仍没又发现错误,那就当是AC了吧,嘿嘿。
- End:
- 一天又过去了,感觉下午效率低下呢,除了听了听讲题,就没做什么了。
- 明天要考试,DP专场,有点方,希望弱弱的我不会爆零。