Gym 101470 题解
A:Banks
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 1e5 + 100; int v[N]; int ans = 0; int l[N], r[N]; int main() { freopen("A.in", "r", stdin); int n; scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d", &v[i]); for(int i = 1; i < n; ++i) r[i] = i + 1; r[n] = 1; for(int i = 2; i <= n; ++i) l[i] = i - 1; l[1] = n; int tot = 0, now = 1; while(true){ if(tot == n) break; if(v[now] < 0){ v[r[now]] += v[now]; v[l[now]] += v[now]; v[now] = -v[now]; tot = 1, ans++; } else{ tot++; } now = r[now]; } printf("%d\n", ans); return 0; }
B:Circle of digits
题意:有一个环形的字符串,现在让你把它切成n段,要求切了之后,所有的串的最大值最小。
题解:我们可以知道 这长度为m的字符串中 最大值的那个字符串长度一定是m = n/k(向上取整), 故答案一定是从环上的每个点出发,然后走m步的串中的一个。 然后我们对这些答案排序, 排完序之后,二分答案。 然后对于每次二分完答案,我们都去check答案, 每次都往从1号位置枚举起点,枚举到m就够了,因为从m+1的位置出发一定是前面的一段的一个过程了,就不需要继续枚举答案了。
然后现在有一个问题就是比较, 对于这个字符串来说, 我们hash之后比较字符串的大小, 先二分hash值,找到第一个能使得这段hash值不相同的地方,然后再比较这个位置的信息就好了。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 2e5 + 100; ULL base = 131; char s[N]; ULL Hash[N], Hash_val[N]; int n, m, k; void init(){ Hash[0] = 1; for(int i = 1; i < N; ++i) Hash[i] = Hash[i-1] * base; for(int i = 1; i <= 2*n; ++i) Hash_val[i] = Hash_val[i-1] * base + s[i] - '0'; } ULL Get_Hash(int l, int r){ return Hash_val[r] - Hash_val[l-1] * Hash[r-l+1]; } int A[N]; bool cmp(int L1, int L2){ int l = 1, r = m; while(l <= r){ int mid = l+r >> 1; if(Get_Hash(L1, L1+mid-1) == Get_Hash(L2, L2+mid-1)) l = mid + 1; else r = mid - 1; } if(l > m) return false; return s[L1+l-1] < s[L2+l-1]; } bool cmp2(int i, int j){ return cmp(i, j); } bool check(int x){ for(int i = 1; i <= m; ++i){ int b = i; for(int j = 1; j <= k; ++j){ if(cmp(x,b) == false) b = b + m; else b = b + m - 1; } if(b-i >= n) return true; } return false; } int main(){ freopen("B.in","r",stdin); scanf("%d%d", &n, &k); m = (n+k-1)/k; scanf("%s", s+1); for(int i = n+1; i <= n*2; ++i) s[i] = s[i-n]; s[2*n+1] = '\0'; init(); for(int i = 1; i <= n; i++) A[i] = i; sort(A+1, A+1+n, cmp2); int l = 1, r = n; while(l <= r){ int mid = l+r >> 1; if(check(A[mid]) == false) l = mid + 1; else r = mid - 1; } for(int i = A[l]; i <= A[l]+m-1; ++i) printf("%c", s[i]); return 0; }
C: UFO
题意: 现在有n×m的一个矩形,每个格子内都会有若干个正方体。 现在射k次激光,每次激光输入为 ( c x h) c 是 方向 x是第几行/列, 然后射掉r个正方体, 射完之后正方体会掉下来, 现在问你射完k次激光之后,矩形内选出一个p*p的正方形, 这里面的最大的值是多少。
题解:
建立n+m棵线段树, 每次修改的时候 找到 [L,R]的这个区间内部中 >= h的的最左或者最右格子的位置是在哪里, 然后在2棵对应的树上修改对应的位置。
然后再进行下一次查询,从新的[L,R]区间中重新找。
然后再把矩形还原回来,再找到最大子矩形。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 1e6 + 100; int n, m, r, k, P; vector<int> vc[N]; vector<int> xtree[N]; vector<int> ytree[N]; vector<int> *p; int op, g; void build(int l, int r, int rt){ if(l == r){ if(op) (*p)[rt] = vc[g][l]; else (*p)[rt] = vc[l][g]; return ; } int m = l+r >> 1; build(lson); build(rson); (*p)[rt] = max((*p)[rt<<1], (*p)[rt<<1|1]); } int Lpos; void FindL(int L, int l, int r, int rt, int h){ if((*p)[rt] < h) return ; if(l == r) { Lpos = min(Lpos, l); return ; } int m = l+r >> 1; if(L <= l){ if((*p)[rt<<1] >= h) FindL(L,lson,h); else FindL(L,rson,h); } else if(L > m) FindL(L,rson,h); else if(L <= m){ if((*p)[rt<<1] >= h) FindL(L,lson,h); if((*p)[rt<<1|1] >= h) FindL(L,rson,h); } } int Rpos; void FindR(int R, int l, int r, int rt, int h){ if((*p)[rt] < h) return ; if(l == r){ Rpos = max(Rpos, r); return ; } int m = l+r >> 1; if(R >= r){ if((*p)[rt<<1|1] >= h) FindR(R,rson,h); else FindR(R,lson,h); } else if(R <= m) FindR(R,lson,h); else if(R > m){ if((*p)[rt<<1|1] >= h) FindR(R,rson,h); if((*p)[rt<<1] >= h) FindR(R,lson,h); } } void Change(int L, int l, int r, int rt){ if(l == r){ (*p)[rt]--; return ; } int m = l+r >> 1; if(L <= m) Change(L,lson); else Change(L,rson); (*p)[rt] = max((*p)[rt<<1], (*p)[rt<<1|1]); } void boom(int l, int r, int rt){ if(l == r){ vc[g][l] = (*p)[rt]; return ; } int m = l+r >> 1; boom(lson); boom(rson); } int main(){ freopen("C.in","r",stdin); scanf("%d%d%d%d%d", &n, &m, &r, &k, &P); for(int i = 0; i <= n; ++i){ vc[i].resize(m+10); xtree[i].resize((m+10)<<2); } for(int i = 1; i <= m; ++i) ytree[i].resize((n+10)<<2); for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j){ scanf("%d", &vc[i][j]); } op = 1; for(int i = 1; i <= n; ++i){ p = &xtree[i]; g = i; build(1,m,1); } op = 0; for(int i = 1; i <= m; ++i){ p = &ytree[i]; g = i; build(1,n,1); } char op[3]; int x, h; while(k--){ scanf("%s", op); scanf("%d%d", &x, &h); if(op[0] == 'N') { int lst = 1; for(int i = 1; i <= r; i++){ Lpos = inf; p = &ytree[x]; FindL(lst, 1, n, 1, h); if(Lpos == inf) break; Change(Lpos, 1, n, 1); p = &xtree[Lpos]; Change(x, 1, m, 1); lst = Lpos + 1; if(lst > n) break; } } else if(op[0] == 'W'){ int lst = 1; for(int i = 1; i <= r; i++){ Lpos = inf; p = &xtree[x]; FindL(lst, 1, m, 1, h); if(Lpos == inf) break; Change(Lpos, 1, m, 1); p = &ytree[Lpos]; Change(x, 1, n, 1); lst = Lpos + 1; if(lst > m) break; } } else if(op[0] == 'E'){ int lst = m; for(int i = 1; i <= r; i++){ Rpos = 0; p = &xtree[x]; FindR(lst, 1, m, 1, h); if(Rpos == 0) break; Change(Rpos, 1, m, 1); p = &ytree[Rpos]; Change(x, 1, n, 1); lst = Rpos - 1; if(lst < 1) break; } } else if(op[0] == 'S'){ int lst = n; for(int i = 1; i <= r; i++){ Rpos = 0; p = &ytree[x]; FindR(lst, 1, n, 1, h); if(Rpos == 0) break; Change(Rpos, 1, n, 1); p = &xtree[Rpos]; Change(x, 1, m, 1); lst = Rpos - 1; if(lst < 1) break; } } } for(int i = 1; i <= n; ++i){ p = &xtree[i]; g = i; boom(1,m,1); } for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j){ vc[i][j] += vc[i][j-1]; if(vc[i][j] >= mod) vc[i][j] -= mod; } int ans = 0; for(int j = 1; j <= m; ++j) for(int i = 1; i <= n; ++i){ vc[i][j] += vc[i-1][j]; if(vc[i][j] >= mod) vc[i][j] -= mod; if(i >= P && j >= P){ int tmp = vc[i][j]; tmp -= vc[i-P][j]; tmp -= vc[i][j-P]; tmp += vc[i-P][j-P]; while(tmp < 0) tmp += mod; while(tmp >= mod) tmp -= mod; ans = max(tmp, ans); } } printf("%d\n", ans); return 0; } /* 4 8 2 6 2 1 1 1 1 1 1 1 1 1 2 3 1 1 1 3 1 1 2 1 1 3 1 1 1 1 1 1 1 1 1 1 2 N 2 2 W 2 2 W 2 3 E 2 1 S 4 1 S 7 1 */
D:Frame
题意:现在有若干个 1 × k 的长方形条, 现在问能不能刚好对 n × m 的矩形的最外面一圈填满。
题解:check一下左上角的格子是竖着填还是横着填的就好了。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("d.in","r",stdin); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 1e5 + 100; int n, m, x; bool check1(int x){ int mm = m - 1; mm %= x; if(mm > 1) return false; int nn = n % x; if(nn > 1) return false; int mmm; if(nn == 1) mmm = m; else mmm = m - 1; mmm %= x; if(mmm > 1) return false; int nnn = n - 2 + mm + mmm; nnn %= x; if(nnn) return false; return true; } bool check2(int x){ int mm = m; mm %= x; if(mm > 1) return false; int nn = n - 1; nn %= x; if(nn > 1) return false; int mmm = m - 1 + nn; mmm %= x; if(mmm > 1) return false; int nnn = n - 2 + mm + mmm; nnn %= x; if(nnn) return false; return true; } int main(){ int T; Fopen; scanf("%d%d", &n, &m); scanf("%d", &T); while(T--){ scanf("%d", &x); if(check1(x) || check2(x)) { puts("YES"); } else puts("NO"); } return 0; }
E:Points
题意:现在2维平面上有若干个点,现在要求找到一个多边形,使得长度最小,并且所有的点都在矩形的内部,不包括边上。 多边形的边只能是格子的对角线或者是沿着格子的边。
题解:先找到一个最小矩形,能把所有点都包括在内部的矩形。 然后再从4个角考虑, 往里缩。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("E.in","r",stdin); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 1e5 + 100; pll p[N]; int main(){ Fopen; int n; int x1, y1, x2, y2; scanf("%d", &n); for(int i = 1; i <= n; ++i) { scanf("%d%d", &p[i].fi, &p[i].se); if(i == 1) x1 = x2 = p[i].fi, y2 = y1 = p[i].se; x1 = min(p[i].fi, x1); y1 = min(p[i].se, y1); x2 = max(p[i].fi, x2); y2 = max(p[i].se, y2); } x1--, y1--, x2++, y2++; LL ans = 2ll*(x2-x1 + y2-y1); LL cnt = 0; int maxx = x1, xx; for(int i = 1; i <= n; ++i){ xx = p[i].fi - (p[i].se - y1); maxx = max(maxx, xx); } maxx++; ans -= (x2-maxx) * 2; cnt += x2-maxx; int minx = x2; for(int i = 1; i <= n; ++i){ xx = p[i].fi + (p[i].se - y1); minx = min(minx, xx); } minx--; ans -= (minx-x1) * 2; cnt += minx-x1; maxx = x1; for(int i = 1; i <= n; ++i){ xx = p[i].fi - (y2 - p[i].se); maxx = max(maxx, xx); } maxx++; ans -= (x2 - maxx) * 2; cnt += x2 - maxx; minx = x2; for(int i = 1; i <= n; ++i){ xx = p[i].fi + (y2 - p[i].se); minx = min(minx, xx); } minx--; ans -= (minx - x1) * 2; cnt += minx - x1; //cout << cnt << endl; double tt = sqrt(2) * cnt + ans; printf("%f", tt); return 0; }
F:Most Influential Pumpkin
题意:现在有一个长度为n的数组,现在有k次操作,给[l, r]这个区间里面的所有数+1, 现在问你每次操作之后的中位数大小是多少。
题解:
暴力分块。 修改的时候就对散的块暴力重构,整个块就打个lz标记。
对于查询来说,我们最容易想到的就是2分套2分,然后找到答案。
但是这个复杂度实在太大,最终会T。
可以发现他每次只会对区间里面的数执行+操作,并且是+1。
所以我们可以发现每次中位数要么是上次修改之后的值,要么是比上次修改的值刚好大1。
这样我们最后每次操作的复杂度就是 (√n + √nlg√n + √nlg√n) 这样的复杂度就说的过去了。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("F.in","r",stdin); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 6e4 + 100; int n, m, blo, num; int a[N], b[N]; int l[N], r[N], lz[N]; void update(int x){ for(int i = l[x]; i <= r[x]; ++i) b[i] = a[i]; sort(b+l[x], b+r[x]+1); } void build(){ blo = sqrt(n) + 1; num = n / blo; if(n%blo) num++; for(int i = 1; i <= num; i++){ l[i] = blo * (i-1) + 1; r[i] = blo * i; lz[i] = 0; } r[num] = n; for(int i = 1; i <= num; i++) update(i); } void Add(int ll, int rr){ int b1 = (ll + blo -1)/ blo; int b2 = (rr + blo -1)/ blo; for(int i = b1+1; i < b2; ++i) ++lz[i]; if(b1 != b2){ for(int i = ll; i <= r[b1]; ++i) ++a[i]; update(b1); for(int i = l[b2]; i <= rr; ++i) ++a[i]; update(b2); } else { for(int i = ll; i <= rr; ++i) ++a[i]; update(b1); } } int boom(int x){ int ret = 0; for(int i = 1; i <= num; ++i){ int tmp = x - lz[i]; int pos = upper_bound(b+l[i], b+r[i]+1, tmp) - b - l[i]; ret += pos; } return ret; } int need = 0; int mid; int c[N]; int main(){ Fopen; while(~scanf("%d%d", &n, &m) && n+m){ need = n / 2 + 1; for(int i = 1; i <= n; ++i){ scanf("%d", &a[i]); c[i] = a[i]; } sort(c+1, c+1+n); mid = c[need]; build(); for(int i = 1,ll,rr; i <= m; ++i){ scanf("%d%d",&ll,&rr); Add(ll,rr); if(boom(mid) < need) mid++; printf("%d\n", mid); } } return 0; }
H:Triples
题解:
我们明白 a^j + b^j = c^j 当 a,b,c > 0 && j > 2上是无解的。
对于j==2的时候我们暴力求解。
然后对于j>3的时候 我们明白只有 a = 0, b = c 这种才合法,直接计算就好了。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("H.in","r",stdin); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 1e5 + 100; int main(){ int ans = 0; int n, m; Fopen; scanf("%d%d", &m, &n); //n = n*n*n; for(int i = 0; i <= m; ++i) for(int j = i; j <= m; ++j) for(int k = j; k <= m; ++k){ if(i*i+j*j == k*k) ans++; } //int cnt = 0; ans += (m+1) * (n-2); printf("%d\n", ans); return 0; }
J:Strange Antennas
题意:求被辐射的点。
题解:维护每行来说点的变数, 就是维护出对于每个位置来说,哪一列会对他产生影响,哪一列会消除影响。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 3e4 + 100; vector<int> vc[N]; int n, m; int vis[N]; inline bool check(int x, int y){ if(x < 1 || y < 1 || x > n || y > n) return false; return true; } int main(){ freopen("J.in","r",stdin); scanf("%d%d", &n, &m); for(int i = 1; i <= m; ++i){ int x, y, r, op; scanf("%d%d%d%d", &x, &y, &r, &op); if(op == 1){ for(int i = 0; i < r; ++i){ if(check(x+1,y+1+i)){ vc[x+1].pb(y+1+i); } } x = x + 2; y = y + r; while(r--){ if(check(x,y)) vc[x].pb(y); ++x; --y; } } else if(op == 0){ ++x; for(int i = 0; i < r; ++i){ if(check(x,y-i)) vc[x].pb(y-i); } x++, y = y - r + 1; while(r--){ if(check(x,y)) vc[x].pb(y); ++x;++y; } } else if(op == 2){ ++y; for(int i = 0; i < r; ++i) if(check(x+1,y+i)) vc[x+1].pb(y+i); x = x - r + 1; while(r--){ if(check(x,y)) vc[x].pb(y); else if(check(1,y)) vc[1].pb(y); ++x;++y; } } else if(op == 3){ for(int i = 0; i < r; ++i) if(check(x+1,y-i)) vc[x+1].pb(y-i); x = x - r + 1; while(r--){ if(check(x,y)) vc[x].pb(y); else if(check(1,y)) vc[1].pb(y); x++, y--; } } } int ans = 0; int cnt = 0; for(int i = 1; i <= n; i++){ for(auto y : vc[i]){ vis[y] ^= 1; if(vis[y]) cnt++; else cnt--; } ans += cnt; } printf("%d\n", ans); return 0; }