沈阳集训day6
Posted edsheeran
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了沈阳集训day6相关的知识,希望对你有一定的参考价值。
问题 A: YY的矩阵
题目描述
输入
输出
样例输入
4 4
4 4 10 7
2 13 9 11
5 7 8 20
13 20 8 2
4
1 1 4 4
1 1 3 3
1 3 3 4
1 1 1 1
样例输出
20
13
20
4
提示
数据范围:
60%的数据:N×M×P<10^8
100%的数据:1 <= N, M <= 300;1 <= P <= 1,000,000;1 <= r1 <= r2 <= N, 1 <= c1 <= c2 <= M,1<=W[i][j]<=10000.
题解:二维ST表,O(1)查询,log要开成N那么大
#include <bits/stdc++.h> using namespace std; int n, m, a[305][305][10][10], lo[310]; void init(){ for(int pi = 0; pi < 10; pi++) for(int pj = 0; pj < 10; pj++) if(!pi && !pj)continue; else for(int i = 1; i + (1<<pi) -1 <= n; i++) for(int j = 1; j + (1<<pj) -1 <= m; j++){ if(!pi) a[i][j][pi][pj] = max(a[i][j][pi][pj - 1], a[i][j + (1<<(pj-1))][pi][pj - 1]); else a[i][j][pi][pj] = max(a[i][j][pi - 1][pj], a[i + (1<<(pi-1))][j][pi - 1][pj]); // printf("%d %d %d %d %d ", i,j,pi,pj,a[i][j][pi][pj]); } } int query(int x1, int y1, int x2, int y2){ int pi = lo[x2 - x1 + 1], pj = lo[y2 - y1 + 1]; int ans1 = max(a[x1][y1][pi][pj], a[x2 - (1<<pi) +1][y1][pi][pj]); int ans2 = max(a[x1][y2 - (1<<pj) + 1][pi][pj], a[x2 - (1<<pi) + 1][y2 - (1<<pj) + 1][pi][pj]); return max(ans1 ,ans2); } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) scanf("%d", &a[i][j][0][0]); lo[0] = -1; for(int i = 1; i <= 305; i++)lo[i] = lo[i/2] + 1; init(); int q; scanf("%d", &q); while(q--){ int x1, x2, y1, y2; scanf("%d%d%d%d", &x1, &y1, &x2, &y2); int ans = query(x1, y1, x2, y2); printf("%d ", ans); } return 0; }
问题 B: WYT的刷子(一道单调队列的好题)
题目描述
输入
输出
样例输入
Input1:
5 3
5 3 4 4 5
Input2:
10 3
3 3 3 3 3 3 3 3 3 3
Input3:
7 4
1 2 3 4 3 2 1
样例输出
Output1:
3
2
Output2:
0
4
Output3:
4
4
提示
样例1的解释:
数据范围:
30%的数据:N<=10^3
50%的数据:N<=10^5
100%的数据:1<=N<=10^6, 1<=M<=10^6,N>=M, 每列栅栏的高度<=10^6.
题解:两个单调队列,一个维护区间内最小值,一个在区间内维护最大值,就可以得到可以涂的轮廓线;(自己画图)
遇到高度不一样或长度不够则要多一个刷子;
单调对列求区间最大用递减队列;
#include<bits/stdc++.h> using namespace std; #define ll long long const int M = 1e6+10; struct Queue{ int id; ll h; }q[M], q2[M], q3[M]; ll a[M], lim[M]; int main(){ int n, m; ll ret = 0; scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++)scanf("%lld", &a[i]); int h = 1, t = 0; for(int i = 1; i <= n; i++){ while(h <= t && q[t].h >= a[i])t--; q[++t].id = i; q[t].h = a[i]; if(i >= m){ if(q[h].id <= i - m)h++; q2[i].h = q[h].h; q2[i].id = q[h].id; } } for(int i = 1; i < m; i++)q2[i].id = i, q2[i].h = -1e8; h = 1, t = 0; for(int i = 1; i <= n; i++){ while(h <= t && q3[t].h <= q2[i].h)t--; q3[++t].id = i; q3[t].h = q2[i].h; if(i >= m){ if(q3[h].id <= i - m)h++; lim[i - m + 1] = q3[h].h; ret += a[i - m + 1] - lim[i - m + 1]; } } ll tt = q2[n].h; for(int i = n; i > n - m + 1; i--){ tt = max(tt, q2[i].h); lim[i] = tt; ret += a[i] - lim[i]; } //for(int i = 1; i <= n; i++)printf("%d ", lim[i]); int lst = 1, cnt = 1; for(int i = 1; i <= n; i++){ if(i - lst + 1 > m || lim[lst] != lim[i]){ lst = i; cnt++; } } printf("%lld %d ", ret, cnt); }
问题 C: 2017种树
题目描述
输入
输出
样例输入
5
10
3
1
1
样例输出
180
提示
样例解释:
5棵树的位置分别为: 3, 4, 5, 6, 7.
费用分别为: 1, 3, 6, 10. (从第一棵树开始)
总费用为: 1 × 3 × 6 × 10 = 180.
数据范围:
10%的数据:N<=10;
60%的数据:N<=5×10^4;
100%的数据:N,L<=200000; X[0] ,A, B<=10^9.
题解:值域线段树
一个树与其他树距离和:他离开头的距离*在他前面树的个数 - 前面树离开头距离总和 + 他离结尾的距离*在他后面树的个数 - 后面树离结尾距离总和;用值域线段树记录前面树的个数距离,log(N)求一棵树离其他树的距离,总复杂度Nlog(N);
注意到处都要mod, 还要防止减成负数,还有nd->zuo = (nd->zuo + pos) % mod; 不能直接赋值成位置,因为有多个树,这个地方wa了很久
#include <bits/stdc++.h> #define ll long long using namespace std; const int M = 200005; const ll mod = 1000000007; int cnt, m; ll dis[M], tmp[M]; ll ans = 1; struct Node{ ll zuo, you; int zz; Node *ls, *rs; void up(){ zuo = (ls->zuo + rs->zuo) % mod; you = (ls->you + rs->you) % mod; zz = ls->zz + rs->zz; //yy = ls->yy + rs->yy; } }pool[M<<2], *root, *tail = pool; Node * build(int l = 1, int r = m){ Node * nd = ++tail; if(l == r)nd->zuo = nd->you = nd->zz = 0; else { int mid = (l + r) >> 1; nd->ls = build(l, mid); nd->rs = build(mid+1, r); nd->up(); } return nd; } #define Ls nd->ls, l, mid #define Rs nd->rs, mid+1, r ll query1(int L, int R, Node * nd = root, int l = 1, int r = m){ if(L <= l && R >= r){ cnt += nd->zz; return nd->zuo; } int mid = (l + r) >> 1; ll ans = 0; if(L <= mid) ans = query1(L, R, Ls); if(R > mid) ans = (ans+ query1(L, R, Rs)) % mod; return ans; } ll query2(int L, int R, Node * nd = root, int l = 1, int r = m){ if(L <= l && R >= r){ cnt += nd->zz; return nd->you; } int mid = (l + r) >> 1; ll ans = 0; if(L <= mid) ans = query2(L, R, Ls); if(R > mid) ans = (ans+ query2(L, R, Rs)) % mod; return ans; } void insert(int pos, ll d2, Node * nd = root, int l = 1, int r = m){ if(l == r){ nd->zz ++; nd->zuo = dis[pos]; nd->you = d2; } else { int mid = (l + r) >> 1; if(pos <= mid)insert(pos, d2, Ls); else insert(pos, d2, Rs); nd->up(); } } int main() { int n; ll L, A, B; scanf("%d%lld%lld%lld%lld", &n, &L, &dis[1], &A, &B); dis[1] %= L; tmp[1] = dis[1]; for(int i = 2; i <= n; i++)tmp[i] = dis[i] = (dis[i-1]*A+B) % L; // cout<<"OO"; sort(tmp+1, tmp+1+n); m = unique(tmp+1, tmp+1+n) - tmp - 1; root = build(); // cout<<"h1"<<endl; for(int i = 1; i <= n; i++){ int pos = lower_bound(tmp+1, tmp+1+m, dis[i]) - tmp; ll here = 0; cnt = 0; ll a1 = query1(1, pos); // cout<<"L"; here = ( (1LL*cnt*dis[i] % mod - a1) + mod * 2 ) % mod; cnt = 0; ll a2 = query2(pos, m); // cout<<"KK"; ll dd = tmp[m] - dis[i]; here = (here + ( (1LL*cnt*dd%mod - a2) % mod) + mod * 2) % mod; if(!here && i == 1)here = 1; insert(pos, dd); ans = (ans * here) % mod; //cout<<ans<<endl; } printf("%lld ", ans); return 0; }
以上是关于沈阳集训day6的主要内容,如果未能解决你的问题,请参考以下文章