今天是真NOIP难度的题
第一题:单调栈原题,但被读优卡成了30,以后一定要注意读入问题
#include<bits/stdc++.h> using namespace std; const int M = 2005; int a[M][M], up[M][M]; struct node{int w, h;}q[M]; int read(){ int x = 0; int f = 1; char c = getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();} return x*=f; } int main(){ //freopen("cc.in","r",stdin); freopen("inspect.in","r",stdin); freopen("inspect.out","w",stdout); int n, m, ans = 0; scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++)a[i][j] = read(); for(int i = n; i; i--) for(int j = 1; j <= m; j++) up[i][j] = a[i][j] ? up[i+1][j] + 1 : 0; for(int st = 1; st <= n; st++){ int h = 1, t = 1; q[1].w = 1, q[1].h = up[st][1]; for(int i = 2; i <= m; i++){ int w = 0; while(h <= t && up[st][i] <= q[t].h){ w += q[t].w; ans = max(ans, min(w, q[t].h)); t--; } q[++t] = (node) {1 + w, up[st][i]}; } int w = 0; while(h <= t){ w += q[t].w; ans = max(ans, min(w, q[t].h)); t--; } } printf("%d ", ans); }
第二题:开始想贪心,看到数据觉得可能不是,然后感觉好像方格取数加强版,就上了一波费用流,其实就是最基本的最小链覆盖;
但是后来有人贪心过了,把x排序以后不就变成了导弹拦截。。。
#include<bits/stdc++.h> using namespace std; const int N = 705, M = 3e5, inf = 1e9; int tot = 1, S, T, SS; int h[N], dis[N], px[N], py[N], pre[N], pree[N], n; bool inq[N]; struct zz{int x,y;}qq[N]; bool cmp(zz A, zz B){return A.x == B.x ? A.y < B.y : A.x < B.x;} struct edge{int v, nxt, w, pf, f;}G[M]; void add(int u, int v, int f, int w){ G[++tot].v = v, G[tot].nxt = h[u], G[tot].w = w, G[tot].pf = G[tot].f = f, h[u] = tot; G[++tot].v = u, G[tot].nxt = h[v], G[tot].w = -w, G[tot].pf = G[tot].f = 0, h[v] = tot; } #define RG register queue <int> q; bool spfa(){ memset(dis, 0x8f, sizeof(dis)); memset(pre, 0, sizeof(pre)); memset(pree, 0, sizeof(pree)); dis[SS] = 0, inq[SS] = 1; q.push(SS); while(!q.empty()){ int u=q.front();q.pop();inq[u]=0; for(RG int i = h[u]; i; i = G[i].nxt){ int v = G[i].v; if(G[i].f && dis[v] < dis[u] + G[i].w){ dis[v] = dis[u] + G[i].w; pre[v] = u, pree[v] = i; if(!inq[v])q.push(v), inq[v] = 1; //fprintf(stderr, "%d %d %d %d %d ", u, v, dis[u], dis[v], G[i].w); } } } return dis[T] > 0; } int check(int k){ G[tot].f = 0, G[tot^1].f = k; int ret = 0; for(int i = 1; i <= tot - 2; i++)G[i].f = G[i].pf; while(spfa()){ int u = T, del = inf; while(u != SS){ del = min(del, G[pree[u]].f); u = pre[u]; } u = T; while(u != SS){ G[pree[u]].f -= del; G[pree[u]^1].f += del; u = pre[u]; } ret += dis[T]*del; } return ret; } int main(){ //freopen("cc.in","r",stdin); freopen("militarytraining.in","r",stdin); freopen("militarytraining.out","w",stdout); scanf("%d", &n); S = 0, T = 2 * n + 1, SS = T + 1; for(int i = 1; i <= n; i++) scanf("%d%d", &qq[i].x, &qq[i].y); sort(qq + 1, qq + 1 + n, cmp); int cnt = 0; for(RG int i = 1; i <= n; i++) if(qq[i].x == qq[i-1].x && qq[i].y == qq[i-1].y)continue; else px[++cnt] = qq[i].x, py[cnt] = qq[i].y; for(RG int i = 1; i <= cnt; i++) for(RG int j = 1; j <= cnt; j++) if(i != j && px[i] <= px[j] && py[i] <= py[j]) add(i + n, j, inf, 0); for(RG int i = 1; i <= cnt; i++){ add(S, i, 1, 0); add(i + n, T, 1, 0); add(i, i + n, 1, 1); } add(SS, S, 0, 0); int lf = 1, rg = cnt, ans = 0; while(lf <= rg){ int mid = (lf + rg) >> 1; if(check(mid) == cnt) ans = mid, rg = mid - 1; else lf = mid + 1; } printf("%d ", ans); }
第三题:思维好题
考虑将询问按左端点排序,从右向左做。
维护一个数组t,t[i]表示如果询问区间包含了点i,答案会增加t[i](可能为负)。
初始情况下t全为0,i从n枚举到1,对某个i,考虑a[i]这个数在i位置及其以后是否出现过a[i]次及以上,假设a[i]在位置x出现了第a[i]次,在位置y出现了第a[i]+1次,即表示对于左端点为i的询问区间,当右端点在[x,y)时,a[i]会贡献1的答案,否则贡献0的答案,此时设t[x]=1且t[y]=-1即可。
用一个树状数组维护t数组,可以很容易的统计前缀和。
复杂度为O(nlogn+qlogn+qlogq)。
#include<bits/stdc++.h> using namespace std; const int M = 1e6 + 5; int lst[M], a[M], n, c[M], ans[M], cnt[M], que[M], gx[M]; vector <int> vec[M]; #define RG register struct node{int l, r, id;}q[M]; bool cmp(node A, node B){ return A.l < B.l; } int read(){ int x = 0; int f = 1; char c = getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();} return x*=f; } void add(int x, int d){ for(; x <= n; x += x&(-x)) c[x] += d; } int query(int x){ int ret = 0; for(; x; x -= x&(-x)) ret += c[x]; return ret; } int main(){ freopen("count.in","r",stdin); freopen("count.out","w",stdout); n = read();int Q = read(), tot = 0; for(RG int i = 1; i <= n; i++){ a[i] = read(); cnt[a[i]]++; vec[a[i]].push_back(i); } for(RG int i = 1; i <= n; i++) if(cnt[i] >= i)que[++tot] = i, gx[i] = 1; for(RG int i = 1; i <= tot; i++){ int u = que[i]; if(vec[u].size() == u) add(vec[u][u-1], 1); else { add(vec[u][u-1], 1); add(vec[u][u], -1); } lst[u] = u; } for(RG int i = 1; i <= Q; i++) q[i].l = read(), q[i].r = read(), q[i].id = i; sort(q + 1, q + 1 + Q, cmp); int lf = 0; for(RG int i = 1; i <= Q; i++){ while(lf < q[i].l){ lf++; int u = a[lf-1]; if(!gx[u])continue; if(lst[u] == (int)vec[u].size()){ add(vec[u][lst[u]-1], -1); lst[u]++; } else if(lst[u] == (int)vec[u].size()-1){ add(vec[u][lst[u]-1], -1); add(vec[u][lst[u]], 2); lst[u]++; } else if(lst[u] <= (int)vec[u].size()-2){ add(vec[u][lst[u]-1], -1); add(vec[u][lst[u]], 2); add(vec[u][lst[u]+1], -1); lst[u]++; } } ans[q[i].id] = query(q[i].r); } for(int i = 1; i <= Q; i++)printf("%d ", ans[i]); }