比赛地址:http://vjudge.net/contest/view.action?cid=47024#overview
这次的题目说难其实不难,但是有些纯粹是模板题的那种东西如果你的数据结构学得不好,那明显是做不来的。我承认这里面大部分都不算是自己写的,毕竟模板这种东西好啊!!
顺便庆祝一下,肖老爷以及李雪夫妇在陕西邀请赛中获得第一╮(╯▽╰)╭!
A题(CodeForces 361A):水题,这题没做出来真是打脸啊( ̄ε(# ̄)☆╰╮( ̄▽ ̄///)其实我一开始也在想什么construct algorithms,后来突然发现
解题报告:
只要在矩阵的对角线上取值为k便得到了答案。
#include <algorithm> #include <iostream> #include <iomanip> #include <cstring> #include <climits> #include <complex> #include <fstream> #include <cassert> #include <cstdio> #include <bitset> #include <vector> #include <deque> #include <queue> #include <stack> #include <ctime> #include <set> #include <map> #include <cmath> using namespace std; int main() { int n,i,j,k; scanf("%d%d",&n,&k); for(i=0;i<n;i++,printf("\n")) { for(j=0;j<n;j++) { if(i==j) { printf("%d ",k); } else { printf("0 "); } } } return 0; }
B题(POJ 2823):有人说可以用线段树搞定这个东西.....其实一个队列就行了啊.....
解题报告:
维护两个单调队列(一个递增,一个递减)且长度不大于k,然后你就只要每次取队列头(top)就是答案。其实吧,用数组和queue都行,只是我STL用得不是很顺畅,所以就选择了用一般的数组。可以用getint()这样自己写的读取函数优化(详见丁神博客)
<pre name="code" class="cpp">#include<iostream> #include<cstdio> using namespace std; const int MAXN=1000010; struct node { int num; int i; }q1[MAXN],q2[MAXN]; int maxn[MAXN],minn[MAXN]; int main() { int n,k,i; while(scanf("%d %d",&n,&k)!=EOF) { int top1,top2,tail1,tail2; top1=top2=tail1=tail2=0; int p; for(i=0;i<n;i++) { scanf("%d",&p); while(top1<tail1&&q1[tail1-1].num<p) tail1--;//递减队列 q1[tail1].i=i,q1[tail1++].num=p; while(i-q1[top1].i>=k) top1++; while(top2<tail2&&q2[tail2-1].num>p) tail2--;//递增队列 q2[tail2].i=i,q2[tail2++].num=p; while(i-q2[top2].i>=k) top2++; if(i>=k-1) maxn[i-k+1]=q1[top1].num,minn[i-k+1]=q2[top2].num; } printf("%d",minn[0]); for(i=1;i<=n-k;i++) printf(" %d",minn[i]); printf("\n%d",maxn[0]); for(i=1;i<=n-k;i++) printf(" %d",maxn[i]); printf("\n"); } return 0; }
C题(POJ 1836):( ̄ε(# ̄)☆╰╮( ̄▽ ̄///),呵呵,写了两个小时,wa了六次的结果是,题目看错了!!!!然后看懂了以后七分钟就搞定了.....
解题报告:
上面这个就是长官想要达到的效果。然后你只要从两端求出最长上升子序列,然后比较一下点I左边和右边的最长上升子序列的和的最大值,然后再用总人数减去该最大值,就是所求答案。
#include <algorithm> #include <iostream> #include <iomanip> #include <cstring> #include <climits> #include <complex> #include <fstream> #include <cassert> #include <cstdio> #include <bitset> #include <vector> #include <deque> #include <queue> #include <stack> #include <ctime> #include <set> #include <cmath> using namespace std; int dp1[1010],dp2[1010]; float num[1010]; int main() { int i,j,n,max; while(scanf("%d",&n)!=EOF) { memset(dp1,0,sizeof(dp1)); memset(dp2,0,sizeof(dp2)); memset(num,0,sizeof(num)); max=0; for(i=1;i<=n;i++) scanf("%f",&num[i]); for(i=1;i<=n;i++) { dp1[i]=1; for(j=i-1;j>=1;j--) if(num[i]>num[j]&& dp1[i]<dp1[j]+1) dp1[i]=dp1[j]+1; } for(i=n;i>=1;i--) { dp2[i]=1; for(j=i+1;j<=n;j++) if(num[i]>num[j]&& dp2[i]<dp2[j]+1) dp2[i]=dp2[j]+1; } for(i=1;i<=n;i++) for(j=i+1;j<=n;j++) if(dp1[i]+dp2[j]>=max) max=dp1[i]+dp2[j]; printf("%d\n",n-max); } return 0; }
D题(POJ 2155):这题嘛,比赛的时候我是百度的(因为最后没有时间了,我就YY了一下)。但是下面贴的代码是我自己后来写的。
解题报告:
二维的树状数组,但是跟新方向和求和方向与一般的不同。update(x,y)是从点(x,y)到点(0,0)进行取反。getsum(x,y)是从该点到(n,n)点进行求和,当然当然,你也可以向前求和,然后再用(n,n)减去前面的几块(容斥原理)。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int MAXN=1010; int c[MAXN][MAXN],n; inline int lowbit(int x) { return x&(-x); } void update(int x,int y) { for(int i=x;i>0;i-=lowbit(i)) for(int j=y;j>0;j-=lowbit(j)) c[i][j]^=1; } int getsum(int x,int y) { int sum=0; for(int i=x;i<MAXN;i+=lowbit(i)) for(int j=y;j<MAXN;j+=lowbit(j)) sum+=c[i][j]; return sum%2; } int main() { int T,q; scanf("%d",&T); while(T--) { scanf("%d %d\n",&n,&q); memset(c,0,sizeof(c)); while(q--) { char ch=getchar(); if(ch=='Q') { int x,y; scanf("%d %d",&x,&y); printf("%d\n",getsum(x,y)); } else { int x1,y1,x2,y2; scanf("%d %d %d %d",&x1,&y1,&x2,&y2); update(--x1,--y1); update(x1,y2); update(x2,y1); update(x2,y2); } getchar(); } puts(""); } return 0; }
E题(POJ 1470):模板题,然后我华华丽丽狂敲了一段RMQ+LCA
解题报告:
百度一下LCA的算法,大概有离线的tarjan,在线的,还有RMQ算法。至于理解大家可以去百度一下吧~
#include <algorithm> #include <iostream> #include <iomanip> #include <cstring> #include <climits> #include <complex> #include <fstream> #include <cassert> #include <cstdio> #include <bitset> #include <vector> #include <deque> #include <queue> #include <stack> #include <ctime> #include <set> #include <cmath> using namespace std; const int N=1008; int vis[N],val[N],p[N]; int first[N*2],node[N*2],dep[N*2],dp[N*2][25]; int mi[25];//移位运算 vector<int>t[N];//邻接表 int lc[N],in[N],n; void dfs(int &index,int u,int d) { index++;//时间搓,全部遍历一次并记录所有节点的父亲,为查找公共祖先做准备 node[index]=u; dep[index]=d; vis[u]=1; first[u]=index; for(int i=0;i<t[u].size();i++) { if(!vis[t[u][i]]) { dfs(index,t[u][i],d+1); index++; node[index]=u; dep[index]=d; } } } void rmq_init(int n)//RMQ预处理 { int K=(int)(log((double)n)/log(2.0)); for(int i=1;i<=n;i++) dp[i][0]=i; for(int j=1;j<=K;j++) for(int i=1; i-1+mi[j]<=n;i++) { int a=dp[i][j-1]; int b=dp[i+mi[j-1]][j-1]; if(dep[a]<dep[b]) dp[i][j] = a; else dp[i][j] = b; } } int rmq(int x,int y) { int k=(int)(log((double)(y-x+1))/log(2.0)); int a=dp[x][k]; int b=dp[y+1-mi[k]][k]; if(dep[a]<dep[b])//最近公共祖先的深度 return a; else return b; } int lca(int a,int b) { int x=first[a]; int y=first[b]; int k; if(x>y)//可用swap,但是我以前用了超时,所以再也不敢用了。 { k=rmq(y,x); return node[k]; } else { k=rmq(x,y); return node[k]; } } int main() { for(int i=0;i<20;i++) mi[i]=1<<i; // freopen("data.txt","r",stdin); while(scanf("%d",&n)!=EOF) { memset(in,0,sizeof(in)); for(int i=0;i<=n;i++) t[i].clear(); for(int i=0;i<n;i++) { int a,b,m; scanf("%d:(%d)",&a,&m); for(int j=0;j<m;j++) { scanf("%d",&b); t[a].push_back(b); t[b].push_back(a); in[b]++; } } memset(vis,0,sizeof(vis)); int tot=0,m; for(int i=1;i<=n;i++) if(!in[i]) dfs(tot,i,1); rmq_init(tot); scanf("%d",&m); getchar(); memset(lc,0,sizeof(lc)); for(int i=0;i<m;i++) { int a,b; scanf(" (%d %d)",&a,&b); lc[lca(a,b)]++; } for(int i=1;i<=n;i++) if(lc[i]) printf("%d:%d\n",i,lc[i]); } return 0; }
F题(POJ 2442):
解题报告:
维护一个最小堆(优先队列),大小为K。读入数据,然后每一层都升序排序。然后就是最上面一层压入堆,然后每一层,先将第一个元素选出和原优先队列的元素求和,这样才能保证是最小的,然后再比较新的一层的其他元素和原序列和的sum,判断是否可以压入优先队列,如果不能够压入优先队列,则跳出(因为后面的元素更大,更不可能压入优先队列)。将所有数处理完后,维护的最小堆就是所求答案。
#include<cstdio> #include<queue> #include<algorithm> using namespace std; const int MAXN=2010; int a[MAXN],b[MAXN],n,m; int main() { int T; scanf("%d",&T); while(T--) { scanf("%d %d",&m,&n); for(int i=0;i<n;i++) scanf("%d",&a[i]); m--; sort(a,a+n); priority_queue< int>q; while(m--) { for(int i=0;i<n;i++) scanf("%d",&b[i]); sort(b,b+n); for(int i=0;i<n;i++)//第一行作为比较 q.push(a[i]+b[0]); for(int i=1;i<n;i++) for(int j=0;j<n;j++) { if(a[j]+b[i]>q.top()) break; q.pop(),q.push(a[j]+b[i]); } for(int i=0;i<n;i++) { a[i]=q.top(); q.pop(); } sort(a,a+n); } for(int i=0;i<n;i++) { printf("%d",a[i]); if(i!=n-1) printf(" "); else printf("\n"); } } return 0; }
G题(POJ 2299):这题在寒假就刷得不爱刷了
解题报告:
可以写一个归并排序,然后求逆序数。
可以写一个离散化(想了一下才想起来)+树状数组
#include <algorithm> #include <iostream> #include <iomanip> #include <cstring> #include <climits> #include <complex> #include <fstream> #include <cassert> #include <cstdio> #include <bitset> #include <vector> #include <deque> #include <queue> #include <stack> #include <ctime> #include <set> #include <map> #include <cmath> using namespace std; const int MAXN=500010; typedef long long ll; struct Node { int x,v; }a[MAXN]; int c[MAXN],p[MAXN]; int n; int lowbit(int x) { return x&(-x); } void updata(int i,int v) { for(;i<=n;i+=lowbit(i)) c[i]+=v; return; } int getsum(int i) { int sum=0; for(;i;i-=lowbit(i)) sum+=c[i]; return sum; } bool cmp(Node a,Node b) { return a.v<b.v; } int main() { while(~scanf("%d",&n)&&n) { for(int i=0;i<n;i++) { scanf("%d",&a[i].v); a[i].x=i+1; } sort(a,a+n,cmp); int id=0; p[a[0].x]=++id; for(int i=1;i<n;i++) if(a[i].v==a[i-1].v) p[a[i].x]=id; else p[a[i].x]=++id; ll ans=0; memset(c,0,sizeof(c)); for(int i=1;i<=n;i++) { updata(p[i],1); ans+=(ll)i-(ll)getsum(p[i]); } printf("%lld\n",ans); } return 0; } /* 5 9 1 0 5 4 3 1 2 3 0 */
H题(POJ 3468):模板题,这个好多书上都用来教学
解题报告:
我用的是两个树状数组,其实是丁神的想法,我自己就不太明白,所以在这里不是很推荐http://blog.csdn.net/winoros/article/details/26352089(证明见丁神博客)。我还是偏向于线段树的处理。
自己百度一个模板吧。
#include <algorithm> #include <iostream> #include <iomanip> #include <cstring> #include <climits> #include <complex> #include <fstream> #include <cassert> #include <cstdio> #include <bitset> #include <vector> #include <deque> #include <queue> #include <stack> #include <ctime> #include <set> #include <map> #include <cmath> using namespace std; const int MAXN=100010; typedef long long ll; ll a[MAXN],s[MAXN],ss[MAXN]; int n; inline int lowbit(int x) { return x&(-x); } void updata(int i,int v,ll *c) { for(;i<=n;i+=lowbit(i)) c[i]+=v; return; } ll getsum(int i,ll *c) { ll sum=0; for(;i;i-=lowbit(i)) sum+=c[i]; return sum; } ll query(int l,int r) { ll ret=0; ret+=a[r]+getsum(r,s)*(r+1)-getsum(r,ss); ret-=a[l]+getsum(l,s)*(l+1)-getsum(l,ss); return ret; } void add(int l,int r,int x) { updata(l+1,x,s); updata(l+1,x*(l+1),ss); updata(r+1,-x,s); updata(r+1,-x*(r+1),ss); } int main() { int q; scanf("%d %d",&n,&q); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); a[i]+=a[i-1]; } getchar(); while(q--) { char ch=getchar(); if(ch=='Q') { int l,r; scanf("%d %d",&l,&r); ll ans=query(l-1,r); printf("%lld\n",ans); } else { int l,r,v; scanf("%d %d %d",&l,&r,&v); add(l-1,r,v); } getchar(); } return 0; }