感谢本次验题人:诸日强。这次出的8题中7题都是比较简单的,6题都是跟着题意搞搞就好了,G题稍微思考下也能很容易想到,只有C是为了防AK而设的。本来B题是现在OJ上的1136题,由于诸日强说有点难度(可能都不会用STL),所以决定换掉了,还有E题原本Ai的范围是10^9不能直接使用数组下标,在诸日强的建议下改成了200000。
题解目录:
【A题】
用一个变量st指向每次相同的字符出现的最左边的位置,那么只要找出max{i-st}即可。这样复杂度O(n)。
C++ O(n)代码:
#include <bits/stdc++.h> using namespace std; const int MAXN = 3000+10; char a[MAXN]; int main() { int t; scanf("%d", &t); while(t--) { int n; scanf("%d%s", &n,a); int ans = 1,la = 0; for(int i = 1; i < n; i++) { if(a[i] != a[i-1]) la = i; ans = max(ans,i-la+1); } printf("%d\n", ans); } return 0; }
当然由于字符串长度只有3000,你可以用两层循环暴力找,复杂度O(N^2)
O(N^2):
C++:
#include <bits/stdc++.h> using namespace std; const int MAXN = 3000+10; char a[MAXN]; int get_ans(int s, int n) { int ans = 0; for(int i = s; i < n; i++) { if(a[s] == a[i]) ans++; else break; } return ans; } int main() { int t; scanf("%d", &t); while(t--) { int n; scanf("%d%s", &n,a); int ans = 1; for(int i = 0; i < n; i++) ans = max(ans,get_ans(i,n)); printf("%d\n", ans); } return 0; }
Java:
import java.io.*; import java.util.*; public class Main { static char a[] = new char[3000+10]; public static int get_ans(int s, int n) { int ans = 0; for(int i = s; i < n; i++) { if(a[s] == a[i]) ans++; else break; } return ans; } public static void main(String args[]) throws Exception { Scanner cin=new Scanner(System.in); int t = cin.nextInt(); for(int c = 0; c < t; c++) { int n = cin.nextInt(); a = cin.next().toCharArray(); int ans = 1; for(int i = 0;i < n;i++) { int tmp = get_ans(i,n); if(ans < tmp) ans = tmp; } System.out.println(ans); } } }
【B题】
跟着题意来就好了。每次超过100,等级+1,记得经验归零。不需要给代码了吧。
【C题】
本次比赛最难的一题,这里给出三种解法:
【一】LCA(最近公共祖先)+可持久化线段树(函数式线段树/主席树):从根结点1开始DFS的同时在树上建立可持久化线段树,维护值域,然后任意两点(x,y)的路径就是x+y-LCA(x,y)-fa[LCA(x,y)]。查询的时候找最大最小值就可以,就是查找第K大类似的。复杂度O(N*log(N))。关于LCA和可持久化线段树可以百度/谷歌学习。
C++:
#include <bits/stdc++.h> using namespace std; typedef long long LL; #define rep(i,a,n) for(int i = a; i < n; i++) #define repe(i,a,n) for(int i = a; i <= n; i++) #define per(i,n,a) for(int i = n; i >= a; i--) #define clc(a,b) memset(a,b,sizeof(a)) const int INF = 0x3f3f3f3f, MAXN = 50000+10, MAXM = MAXN, M = 20*MAXN; int a[MAXN],head[MAXN],tol,nxt[MAXM],to[MAXM]; inline void add_edge(int u, int v) { nxt[tol] = head[u],to[tol] = v; head[u] = tol++; } int cnt, ft[MAXN], fa[MAXN],d[MAXN<<1],num[MAXN<<1];//d数组点数为2*n-1 int rt[MAXN],lc[M],rc[M],sum[M],all,mxa; void insert(int &u, int x, int y, int p)//可持久化线段树插入结点 { lc[++all] = lc[u],rc[all] = rc[u],sum[all] = sum[u]+1; u = all; if(x == y) return; int m = (x+y)>>1; if(p <= m) insert(lc[u],x,m,p); else insert(rc[u],m+1,y,p); } void dfs(int u, int deep)//找到每个点进入和返回时的所有ft,d,num { ft[u] = cnt; d[cnt] = deep, num[cnt++] = u; rt[u] = rt[fa[u]]; insert(rt[u],1,mxa,a[u]); for(int i = head[u]; ~i; i = nxt[i]) { int v = to[i]; //if(fa == v) continue; fa[v] = u; dfs(v,deep+1); d[cnt] = deep, num[cnt++] = u; } } int dp[MAXN<<1][21], dp_num[MAXN<<1][21];//dp_num记录dp对应的点的编号 void st_init() { rep(i,0,cnt) dp[i][0] = d[i], dp_num[i][0] = num[i]; for(int j = 1; (1<<j) <= cnt; j++) { for(int i = 0; i+(1<<j)-1 < cnt; i++) { if(dp[i][j-1] < dp[i+(1<<(j-1))][j-1]) { dp[i][j] = dp[i][j-1]; dp_num[i][j] = dp_num[i][j-1]; } else { dp[i][j] = dp[i+(1<<(j-1))][j-1]; dp_num[i][j] = dp_num[i+(1<<(j-1))][j-1]; } } } } int st_query(int x, int y)//查询d[x~y]中最小值对应的点编号 { if(x > y) swap(x,y); int k = 0; while((1<<(k+1)) <= y-x+1) k++; if(dp[x][k] <= dp[y-(1<<k)+1][k]) return dp_num[x][k]; return dp_num[y-(1<<k)+1][k]; } void init(int rt)//初始化O(n*log(n)) { cnt = all = fa[1] = 0; dfs(rt,0); st_init(); } int lca_query(int x, int y)//x,y的LCA, O(1) { return st_query(ft[x],ft[y]); } int find_mi(int lca, int lcaf,int tx, int ty, int x, int y) { if(x == y) return x; int m = (x+y)>>1; int d = sum[lc[tx]]+sum[lc[ty]]-sum[lc[lca]]-sum[lc[lcaf]]; if(d) return find_mi(lc[lca],lc[lcaf],lc[tx],lc[ty],x,m); return find_mi(rc[lca],rc[lcaf],rc[tx],rc[ty],m+1,y); } int find_mx(int lca, int lcaf,int tx, int ty, int x, int y) { if(x == y) return x; int m = (x+y)>>1; int d = sum[rc[tx]]+sum[rc[ty]]-sum[rc[lca]]-sum[rc[lcaf]]; if(d) return find_mx(rc[lca],rc[lcaf],rc[tx],rc[ty],m+1,y); return find_mx(lc[lca],lc[lcaf],lc[tx],lc[ty],x,m); } int query(int x, int y)//本题询问 { int lca = lca_query(x,y); return find_mx(rt[lca],rt[fa[lca]],rt[x],rt[y],1,mxa)-find_mi(rt[lca],rt[fa[lca]],rt[x],rt[y],1,mxa); } int main() { #ifdef SHY freopen("d:\\1.txt", "r", stdin); freopen("d:\\out.txt", "wb", stdout); #endif int t,count = 0; scanf("%d", &t); while(t--) { int n; scanf("%d", &n); mxa = 0; repe(i,1,n) scanf("%d", &a[i]),mxa = max(mxa,a[i]); clc(head,-1);tol = 0; rep(i,1,n) { int u,v; scanf("%d%d", &u,&v); add_edge(u,v); } init(1); int q; scanf("%d", &q); printf("Case #%d:\n",++count); while(q--) { int x,y; scanf("%d%d", &x, &y); printf("%d\n", query(x,y)); } } return 0; }
Java:
import java.io.*; import java.util.*; public class Main { final static int MAXN = 50000+10, MAXM = MAXN,M = 50*MAXN; static int a[] = new int [MAXN],head[] = new int [MAXN],tol,nxt[] = new int [MAXN],to[] = new int [MAXN]; public static void add_edge(int u, int v) { nxt[tol] = head[u]; to[tol] = v; head[u] = tol++; } static int cnt, ft[] = new int [MAXN], fa[] = new int [MAXN],d[] = new int [MAXN<<1],num[] = new int [MAXN<<1]; static int rt[] = new int [MAXN],lc[] = new int [M],rc[] = new int [M],sum[] = new int [M],all,mxa; public static void insert(int u, int x, int y, int p)//可持久化线段树插入结点 { lc[++all] = lc[u];rc[all] = rc[u];sum[all] = sum[u]+1; u = all; if(x == y) return; int m = (x+y)>>1; if(p <= m) { insert(lc[u],x,m,p); lc[u] = u+1; } else { insert(rc[u],m+1,y,p); rc[u] = u+1; } } static Stack<Integer> s = new Stack<Integer>(); static int vis[] = new int[MAXN],deep[] = new int[MAXN],n; public static void dfs(int uu) { while(!s.empty()) s.pop(); for(int i = 0; i <= n; i++) vis[i] = 0; s.push(uu); while(!s.empty()) { int u = s.peek(); if(0 != vis[u])//从儿子结点返回 { s.pop(); d[cnt] = deep[fa[u]]; num[cnt++] = fa[u]; continue; } /*第一次进入结点u*/ ft[u] = cnt; d[cnt] = deep[u];num[cnt++] = u; vis[u] = 1; rt[u] = rt[fa[u]]; int tmp = all; insert(rt[u],1,mxa,a[u]); rt[u] = tmp+1; for(int i = head[u]; -1 != i; i = nxt[i]) { int v = to[i]; fa[v] = u;deep[v] = deep[u]+1; s.push(v); } } } static int dp[][] = new int[MAXN<<1][21], dp_num[][] = new int[MAXN<<1][21];//dp_num记录dp对应的点的编号 public static void st_init() { for(int i = 0; i < cnt; i++) { dp[i][0] = d[i]; dp_num[i][0] = num[i]; } for(int j = 1; (1<<j) <= cnt; j++) { for(int i = 0; i+(1<<j)-1 < cnt; i++) { if(dp[i][j-1] < dp[i+(1<<(j-1))][j-1]) { dp[i][j] = dp[i][j-1]; dp_num[i][j] = dp_num[i][j-1]; } else { dp[i][j] = dp[i+(1<<(j-1))][j-1]; dp_num[i][j] = dp_num[i+(1<<(j-1))][j-1]; } } } } public static int st_query(int x, int y)//查询d[x~y]中最小值对应的点编号 { if(x > y) {x ^= y;y ^= x; x^= y;} int k = 0; while((1<<(k+1)) <= y-x+1) k++; if(dp[x][k] <= dp[y-(1<<k)+1][k]) return dp_num[x][k]; return dp_num[y-(1<<k)+1][k]; } public static void init(int rt)//初始化O(n*log(n)) { cnt = all = fa[1] = deep[1] = 0; dfs(rt); st_init(); } public static int lca_query(int x, int y)//x,y的LCA, O(1) { return st_query(ft[x],ft[y]); } public static int find_mi(int lca, int lcaf,int tx, int ty, int x, int y) { if(x == y) return x; int m = (x+y)>>1; int d = sum[lc[tx]]+sum[lc[ty]]-sum[lc[lca]]-sum[lc[lcaf]]; if(d != 0) return find_mi(lc[lca],lc[lcaf],lc[tx],lc[ty],x,m); return find_mi(rc[lca],rc[lcaf],rc[tx],rc[ty],m+1,y); } public static int find_mx(int lca, int lcaf,int tx, int ty, int x, int y) { if(x == y) return x; int m = (x+y)>>1; int d = sum[rc[tx]]+sum[rc[ty]]-sum[rc[lca]]-sum[rc[lcaf]]; if(d != 0) return find_mx(rc[lca],rc[lcaf],rc[tx],rc[ty],m+1,y); return find_mx(lc[lca],lc[lcaf],lc[tx],lc[ty],x,m); } public static int query(int x, int y)//本题询问 { int lca = lca_query(x,y); return find_mx(rt[lca],rt[fa[lca]],rt[x],rt[y],1,mxa)-find_mi(rt[lca],rt[fa[lca]],rt[x],rt[y],1,mxa); } static BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out)); public static void main(String args[]) throws Exception { int t = Integer.parseInt(in.readLine()); for(int c = 0; c < t; c++) { n = Integer.parseInt(in.readLine()); mxa = 0; StringTokenizer st = new StringTokenizer(in.readLine()); for(int i = 1; i <= n; i++) { a[i] = Integer.valueOf(st.nextToken()); if(mxa < a[i]) mxa = a[i]; head[i] = -1; } tol = 0; for(int i = 1; i < n; i++) { st = new StringTokenizer(in.readLine()); int u = Integer.valueOf(st.nextToken()); int v = Integer.valueOf(st.nextToken()); add_edge(u,v); } init(1); int q = Integer.parseInt(in.readLine()); out.write("Case #"+(c+1)+":\n"); for(int i = 0; i < q; i++) { st = new StringTokenizer(in.readLine()); int x = Integer.valueOf(st.nextToken()); int y = Integer.valueOf(st.nextToken()); out.write(query(x,y)+"\n"); } } out.flush(); } }
【二 】树链剖分:维护最大值和最小值即可,答案就是他们差。关于树链剖分不会的请百度/谷歌学习。复杂度O( N*log(N)*log(N) );虽然复杂度比可持久化线段树高一个log(N),但是常数要小很多,在这个数据量下反而更快。
C++代码:
#include <bits/stdc++.h> using namespace std; typedef long long LL; #define rep(i,a,n) for(int i = a; i < n; i++) #define repe(i,a,n) for(int i = a; i <= n; i++) #define per(i,n,a) for(int i = n; i >= a; i--) #define clc(a,b) memset(a,b,sizeof(a)) const int INF = 0x3f3f3f3f, MAXN = 50000+10, MAXM = 50000+10; int head[MAXN], tol, nxt[MAXM], to[MAXM]; int a[MAXN];//每个点初始权值 inline void add_edge(int u, int v) { nxt[tol] = head[u], to[tol] = v; head[u] = tol++; } struct TCP{ private: int fa[MAXN];//fa[u]表示u的父亲 int dep[MAXN];//dep[u]表示u的深度 int sz[MAXN];//sz[u]表示以u为根的子树有几个结点 int son[MAXN];//son[u]表示u的所有儿子结点中sz[]最大的那个点(重儿子) int top[MAXN];//top[u]表示u所在的链的顶端节点 int tree[MAXN];//tree[u]表示节点u在线段树中的编号 int pre[MAXN];//pre[x]表示线段树中编号为x的点在树中的点(和tree相反) int cnt;//线段树中点的数量(其实就是n) void dfs1(int u)//预处理fa[],dep[],sz[],son[] { int num = 0; sz[u] = 1; for(int i = head[u]; ~i; i = nxt[i]) { int v = to[i]; if(v == fa[u]) continue; fa[v] = u;dep[v] = dep[u]+1; dfs1(v); if(sz[v] > num) num = sz[v], son[u] = v; sz[u] += sz[v]; } } void dfs2(int u, int num)//预处理top[],tree[],pre[] { tree[u] = cnt, pre[cnt++] = u,top[u] = num; if(-1 == son[u]) return; dfs2(son[u],num);//必须点递归重儿子,否则重链在线段树的编号会不连续 for(int i = head[u]; ~i; i = nxt[i]) { int v = to[i]; if(v != fa[u] && son[u] != v) dfs2(v,v); } } /*--线段树--s*/ int mx[MAXN<<1],mi[MAXN<<1]; inline int id(int x, int y){return x+y|x!=y;} void bulid(int x, int y) { if(x == y) { mx[id(x,y)] = mi[id(x,y)] = a[pre[x]]; return; } int m = (x+y)>>1; bulid(x,m); bulid(m+1,y); mx[id(x,y)] = max(mx[id(x,m)],mx[id(m+1,y)]); mi[id(x,y)] = min(mi[id(x,m)],mi[id(m+1,y)]); } int qmx,qmi; void query(int x, int y, int ql, int qr) { if(ql <= x && y <= qr) { qmx = max(qmx,mx[id(x,y)]); qmi = min(qmi,mi[id(x,y)]); return; } int m = (x+y)>>1, ans = 0; if(ql <= m) query(x,m,ql,qr); if(qr > m) query(m+1,y,ql,qr); } /*--线段树--e*/ public: void init(int rt) { clc(son,-1); dep[rt] = 0, fa[rt] = -1; dfs1(rt); cnt = 0; dfs2(rt,rt); bulid(0,cnt-1); } int tcp_query(int x, int y) { int f1 = top[x], f2 = top[y]; qmi = INF,qmx = 0; while(f1 != f2) { if(dep[f1] < dep[f2]) swap(f1,f2), swap(x,y); query(0,cnt-1,tree[f1],tree[x]); x = fa[f1], f1 = top[x]; } if(dep[x] > dep[y]) swap(x,y); query(0,cnt-1,tree[x],tree[y]); return qmx-qmi; } }tcp; int main() { #ifdef SHY freopen("d:\\1.txt", "r", stdin); #endif int t,count = 0; scanf("%d", &t); while(t--) { int n; scanf("%d", &n); repe(i,1,n) scanf("%d", &a[i]); clc(head,-1);tol = 0; rep(i,1,n) { int u,v; scanf("%d%d", &u, &v); add_edge(u,v); } tcp.init(1); int q; scanf("%d", &q); printf("Case #%d:\n",++count); while(q--) { int x,y; scanf("%d%d",&x,&y); printf("%d\n", tcp.tcp_query(x,y)); } } return 0; }
【三】Link-Cut-Tree:也可以看成动态树问题,维护最大最小值。复杂度均摊O(N*logN)
#include <bits/stdc++.h> using namespace std; typedef long long LL; #define rep(i,a,n) for(int i = a; i < n; i++) #define repe(i,a,n) for(int i = a; i <= n; i++) #define per(i,n,a) for(int i = n; i >= a; i--) #define clc(a,b) memset(a,b,sizeof(a)) const int INF = 0x3f3f3f3f, MAXN = 50000+10, MAXM = 50000+10; int par[MAXN],ch[MAXN][2],fa[MAXN],val[MAXN],mx[MAXN],mi[MAXN]; //par是真实树的父亲,fa是虚树(splay)的父亲 void init()//下标1~n { clc(par,0);clc(ch,0);clc(fa,0); mi[0] = INF; } inline int chd(int u){return ch[fa[u]][1] == u;} inline void setch(int f, int u, int d){ch[f][d] = u,fa[u] = f;} inline void push_up(int u) { mx[u] = max(mx[ch[u][0]],max(mx[ch[u][1]],val[u])); mi[u] = min(mi[ch[u][0]],min(mi[ch[u][1]],val[u])); } inline void rot(int u) { int d = chd(u),y = fa[u]; setch(fa[y],u,chd(y)); setch(y,ch[u][d^1],d); setch(u,y,d^1); push_up(y);push_up(u); } void splay(int u) { int rt = u; while(fa[rt]) rt = fa[rt]; if(rt == u) return; par[u] = par[rt],par[rt] = 0; while(fa[u]) { if(fa[fa[u]] && chd(u) == chd(fa[u])) rot(fa[u]); rot(u); } } void expose(int u) { for(int now = u,la = 0;now;la = now,now = par[now]) { splay(now); par[ch[now][1]] = now;fa[ch[now][1]] = par[la] = 0; setch(now,la,1); push_up(now); } splay(u); } int find_root(int u)//找到splay最左边点,即当前连通树的根 { expose(u); while(ch[u][0]) u = ch[u][0]; return u; } /*=======================有根树============================*/ bool link(int u, int v)//如果x,y不在同一颗子树中,把u所在子树接到v下面 { //if(find_root(u) == find_root(v)) return false; expose(u); par[u] = v; return true; } int query(int x, int y)//如果x,y在同一颗子树中,返回x,y之间路径上点权的最大值 { //if(find_root(x) != find_root(y)) return -1; expose(x); for(int now = y,la = 0;now; la = now,now = par[now]) { splay(now); if(!par[now]) return max(mx[ch[now][1]],max(mx[la],val[now]))-min(mi[ch[now][1]],min(mi[la],val[now]));//now就是LCA(x,y) par[ch[now][1]] = now;fa[ch[now][1]] = par[la] = 0; setch(now,la,1); push_up(now); } } int main() { #ifdef SHY freopen("d:\\1.txt", "r", stdin); #endif int t,count = 0; scanf("%d", &t); while(t--) { init(); int n; scanf("%d", &n); repe(i,1,n) scanf("%d", &val[i]),mx[i] = mi[i] = val[i]; rep(i,1,n) { int u,v; scanf("%d%d", &u, &v); link(v,u); } int q; scanf("%d", &q); printf("Case #%d:\n",++count); while(q--) { int x,y; scanf("%d%d",&x,&y); printf("%d\n", query(x,y)); } } return 0; }
【D题】
应该没有人不会吧。
【E题】
一开始Ai的范围是10^9的,后来为了简单改成了2*10^5。那么就可以直接用一个2*10^5大的数组记录每个编号的出现次数,最后从头到尾扫描一遍这个数组即可。复杂度O(n)。
而当Ai为10^9的时候怎么办?只要把数组换成一颗红黑树或者HASH表即可,而这两个在C++和Java中都是现成提供的。所以很简单,直接给代码。
C++:
#include <bits/stdc++.h> using namespace std; map<int,int> vis; int main() { int t; scanf("%d", &t); while(t--) { vis.clear(); int n; scanf("%d", &n); for(int i = 0; i < n; i++) { int a; scanf("%d", &a); vis[a]++; } printf("%d\n", vis.size()); for(map<int,int>::iterator it = vis.begin(); it != vis.end(); it++) printf("%d %d\n", it->first,it->second); } return 0; }
Java:
import java.io.*; import java.util.*; public class Main { static BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out)); public static void main(String args[]) throws Exception { HashMap<Integer,Integer> vis = new HashMap<Integer,Integer>();//或者使用TreeMap int t = Integer.parseInt(in.readLine()); for(int c = 0; c < t; c++) { vis.clear(); int n = Integer.parseInt(in.readLine()); for(int i = 0; i < n; i++) { int a = Integer.parseInt(in.readLine()); Object tmp = vis.get(a); if(tmp == null) tmp = 0; vis.put(a,((Integer)tmp)+1); } out.write(Integer.toString(vis.size())); out.newLine(); Iterator<Map.Entry<Integer,Integer>> it = vis.entrySet().iterator(); while (it.hasNext()){ Map.Entry<Integer,Integer> entry = it.next(); out.write(Integer.toString(entry.getKey())+" "+Integer.toString(entry.getValue())); out.newLine(); } } out.flush(); } }
【F题】
模拟题,稍微有点烦,看懂了就很好写了。
C++:
#include <bits/stdc++.h> using namespace std; const int MAXN = 3000+10; int p[2],w[2],c[MAXN],d[MAXN],s[MAXN]; //p两个人的现金,w两个人额位置,c和d表示每块地的标价和过路费,s表示每块地的购买状态-1没人买0-A买了1-B买了 void buy(int id) { if(-1 == s[w[id]]) { if(p[id] >= c[w[id]]) p[id] -= c[w[id]],s[w[id]] = id; } else if(id != s[w[id]]) { p[id] -= d[w[id]]; p[id^1] += d[w[id]]; } } int main() { int t; scanf("%d", &t); while(t--) { int n,m; scanf("%d%d", &n, &m); for(int i = 0; i < n; i++) scanf("%d %d", &c[i], &d[i]); p[0] = p[1] = 10000; w[0] = w[1] = 0; memset(s,-1,sizeof(s)); while(m--) { int a,b; scanf("%d%d",&a, &b); w[0] = (w[0]+a)%n; w[1] = (w[1]+b)%n; buy(0);buy(1); } printf("%d %d\n", p[0],p[1]); } return 0; }
Java:
import java.io.*; import java.util.*; public class Main { final static int MAXN = 3000+10; static int p[] = new int[2],w[] = new int[2],c[] = new int[MAXN],d[] = new int[MAXN],s[] = new int[MAXN]; //p两个人的现金,w两个人额位置,c和d表示每块地的标价和过路费,s表示每块地的购买状态-1没人买0-A买了1-B买了 public static void buy(int id) { if(-1 == s[w[id]]) { if(p[id] >= c[w[id]]) { p[id] -= c[w[id]]; s[w[id]] = id; } } else if(id != s[w[id]]) { p[id] -= d[w[id]]; p[id^1] += d[w[id]]; } } public static void main(String args[]) throws Exception { Scanner cin=new Scanner(System.in); int t = cin.nextInt(); while(t-- > 0) { int n = cin.nextInt(),m = cin.nextInt(); for(int i = 0; i < n; i++) { c[i] = cin.nextInt(); d[i] = cin.nextInt(); } p[0] = p[1] = 10000; w[0] = w[1] = 0; for(int i = 0; i < n; i++) s[i] = -1; while(m-- > 0) { int a = cin.nextInt(),b = cin.nextInt(); w[0] = (w[0]+a)%n; w[1] = (w[1]+b)%n; buy(0);buy(1); } System.out.printf("%d %d\n", p[0],p[1]); } } }
【G题】
首先,把这段代码复制进去毫无疑问是超时的(不然还出什么题)。
仔细一想,求的不就是对于每个a[i]找出0~i-1中的最小值mi,然后对每个a[i]-mi求最大值就可以。而这个最小值只需要在从0->n-1找的时候顺便维护下就可以了。复杂度O(n)。
C++:
#include <bits/stdc++.h> using namespace std; const int MAXN = 100000+10; int a[MAXN]; int get_max(int len) { int ans = a[1]-a[0],mi = a[0]; for(int i = 1; i < len; i++) { ans = max(ans,a[i]-mi); mi = min(mi,a[i]); } return ans; } int main() { int t; scanf("%d", &t); while(t--) { int n; scanf("%d", &n); for(int i = 0; i < n; i++) scanf("%d", &a[i]); printf("%d\n", get_max(n)); } return 0; }
Java:
import java.io.*; import java.util.*; public class Main { final static int MAXN = 100000+10; static int a[] = new int[MAXN]; public static int get_max(int len) { int ans = a[1]-a[0],mi = a[0]; for(int i = 1; i < len; i++) { if(ans < a[i]-mi) ans = a[i]-mi; if(mi > a[i]) mi = a[i]; } return ans; } public static void main(String args[]) throws Exception { Scanner cin=new Scanner(System.in); int t = cin.nextInt(); while(t-- > 0) { int n = cin.nextInt(); for(int i = 0; i < n; i++) a[i] = cin.nextInt(); System.out.printf("%d\n", get_max(n)); } } }
本题考察的是你会不会使用循环语句(嗯,就这样,简单吧。)