Gym 101964 题解
B:Broken Watch (别问,问就是队友写的)
代码:
import java.awt.List; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Time; import java.util.ArrayList; import java.util.Scanner; import java.util.Vector; public class Main { static int[]tmp; public static void main(String[] args) { Scanner sc = new Scanner(System.in); tmp = new int[5]; tmp[0] = 1; tmp[1] = 3; tmp[2] = 6; int a, b, c, flag; a = sc.nextInt(); b = sc.nextInt(); c = sc.nextInt(); long n = sc.nextLong(); if (n == 2) { System.out.println("0"); } else { if (a != b && b != c && c != a) flag = 2; else if (a != b || a != c || b != c) flag = 1; else flag = 0; BigInteger mod = BigInteger.valueOf(2).pow(64); if ((n % 2) == 1) { BigInteger ans = BigInteger.valueOf(tmp[flag]); ans = ans.multiply(BigInteger.valueOf(n)); ans = ans.multiply(BigInteger.valueOf(n + 1)); ans = ans.multiply(BigInteger.valueOf(n - 1)); ans = ans.divide(BigInteger.valueOf(24)); System.out.println(ans.mod(mod)); } else { BigInteger ans1 = BigInteger.valueOf(n); ans1 = ans1.multiply(BigInteger.valueOf(n - 2)); ans1 = ans1.divide(BigInteger.valueOf(2)); BigInteger ans2 = BigInteger.valueOf(n); ans2 = ans2.multiply(BigInteger.valueOf(n - 2)); ans2 = ans2.multiply(BigInteger.valueOf(n - 4)); ans2 = ans2.divide(BigInteger.valueOf(24)); BigInteger ans = ans1.add(ans2); ans = ans.multiply(BigInteger.valueOf(tmp[flag])); System.out.println(ans.mod(mod)); } } sc.close(); } }
C: Tree
题意:有一棵数,树上有黑点白点,现在让你从所有黑点中选m个,使得选出的点中,最长的路径长度最小。
题解:把每个边变成一个新的点, 然后已所有点为root, check一下以这个点为根的最长路径最小是多少。
代码:
#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 = 205; vector<int> vc[N]; int vis[N]; int cnt[N]; void dfs(int o, int u, int deep){ if(vis[u]) cnt[deep]++; for(int i = 0; i < vc[u].size(); ++i){ if(vc[u][i] == o) continue; dfs(u, vc[u][i], deep+1); } } int main(){ int n, m, u, v; scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++i) scanf("%d", &vis[i]); for(int i = 1; i < n; ++i){ scanf("%d%d", &u, &v); vc[u].pb(n+i); vc[n+i].pb(u); vc[v].pb(n+i); vc[n+i].pb(v); } int ans = inf; for(int i = 1; i < n+n; i++){ memset(cnt, 0, sizeof(cnt)); dfs(0,i,0); //cnt[0]=0; for(int j = 0; j < n+n; j++){ if(j) cnt[j] += cnt[j-1]; if(cnt[j] >= m){ ans = min(ans, j); break; } } } printf("%d\n", ans); return 0; } /* 6 3 1 1 0 1 1 1 1 2 1 3 1 4 3 5 3 6 9 4 1 0 1 0 1 0 0 1 1 1 2 2 4 2 3 4 5 1 6 6 7 6 8 7 9 */
D:Space Station
题意:一棵树,现在要求你从1号点出发,遍历完所有的边且回到1号点的花费是多少。现在你可以从任意一个点跳到另一个点跳m次,每次的花费是k,求最小花费。
题解:树形dp。
将一次跳跃拆成2次, dp[u][k] 代表的就是在已u为根的子树内跳跃k次之后他的花费是多少。
现在o 是 u的父亲, 我们要把u的信息往上传, 由分析可以得,当跳跃次数为奇数次的时候,我们只需要访问 o -> u的边一次就好了, 否则需要访问o->u的边2次,然后合并就好了。
代码:
#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 = 1100; int n, m, k, T; int head[N], to[N<<1], nt[N<<1], ct[N<<1], tot; inline void add(int u, int v, int w){ to[tot] = v; ct[tot] = w; nt[tot] = head[u]; head[u] = tot++; } LL dp[N][N<<1], tmp[N<<1]; int sz[N]; void dfs(int o, int u){ sz[u] = 1; //dp[0][u] = 0, dp[1][u] = 0; dp[u][0] = 0, dp[u][1] = 0; for(int i = head[u]; ~i; i = nt[i]){ int v = to[i]; if(v == o) continue; dfs(u, v); memset(tmp, INF, sizeof(tmp)); //int t = min(sz[v], m*2); for(int j = 0; j <= sz[u]; ++j){ for(int k = min(sz[v], m*2-j); k >= 0; --k){ if(k&1) tmp[j+k] = min(tmp[j+k], dp[u][j]+dp[v][k]+ct[i]); else tmp[j+k] = min(tmp[j+k], dp[u][j]+dp[v][k]+ct[i]*2); } } sz[u] += sz[v]; for(int i = 0; i <= sz[u]; ++i) dp[u][i] = tmp[i]; } } int main(){ scanf("%d", &T); while(T--){ memset(head, -1, sizeof(head)); tot = 0; scanf("%d%d%d", &n, &m, &k); int u, v, w; for(int i = 1; i < n; ++i){ scanf("%d%d%d", &u, &v, &w); add(u, v, w); add(v, u, w); } dfs(0, 1); LL ans = INF; for(int i = 0; i <= n; i+=2){ ans = min(ans, dp[1][i] + k * 1ll * (i/2)); } printf("%lld\n", ans); } return 0; }
E:fisherman
题意:在2维平面上,有n条鱼,m个渔夫,渔夫的捕获半径为l,即所有欧几里得距离小于l的鱼都会被渔夫捕获,现在要求输出每个渔夫最多能捕获多少鱼。
题解:如果我们从渔夫的角度去圈, 我们明白渔夫是一个菱形, 要求得到菱形里面的点的个数是不好维护的。
现在我们换一种思路,将每条鱼先移动都x轴上,那么鱼还能移动的距离就是 l-y,所以鱼移动的范围就是[x-(l-y), x + (l-y)]。
然后我们把所有在这个里面的渔夫都找出来,这个过程可以用二分,然后用差分标记一下,最后得到答案。
代码:
#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; pll p[N], b[N]; int cc[N], ans[N]; int main(){ int n, m, l; scanf("%d%d%d", &n, &m, &l); for(int i = 1; i <= n; ++i) scanf("%d%d", &p[i].fi, &p[i].se); for(int i = 1; i <= m; ++i){ scanf("%d", &b[i].fi); b[i].se = i; } sort(b+1, b+1+m); for(int i = 1; i <= n; ++i){ int tmp = l - p[i].se; if(tmp < 0) continue; int ll = p[i].fi - tmp; int rr = p[i].fi + tmp; int lll = lower_bound(b+1, b+1+m, pll(ll, 0)) - b; int rrr = upper_bound(b+1, b+1+m, pll(rr, inf)) - b; rrr--; ///cout << ll << " " << rr << " " << lll << " l with r " << rrr << endl; if(lll > rrr) continue; cc[lll]++; cc[rrr+1]--; } for(int i = 1; i <= m; ++i){ cc[i] += cc[i-1]; ans[b[i].se] = cc[i]; } for(int i = 1; i <= m; ++i) printf("%d\n", ans[i]); return 0; } /* 8 4 4 7 2 3 3 4 5 5 1 2 2 1 4 8 4 9 4 6 1 4 9 */
F:Min Max Convert
题解:现在认为 to[i]就是存在 to[i]的位置能把i改造成合法点的, 并且由于路劲不能相交,所以 对于 i + 1来说 to[i+1] >= to[i],然后我们就从左边往右边找答案,然后我们可以发现2点的连线存在左偏或者右偏, 对于左偏,我们要从左到右按次序修改, 对于右偏我们则要从右到左按次序修改。
代码:
#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 a[N], b[N]; vector<pll> vc[2]; struct Node{ int op, l, r; }; vector<Node> ans; int main(){ int n; scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d", &a[i]); for(int i = 1; i <= n; ++i) scanf("%d", &b[i]); int flag = 1; for(int i = 1, x = 1; i <= n; ++i){ while(x <= n && a[x] != b[i]) x++; if(x > n) { flag = 0; break; } if(x > i) vc[0].pb({i,x}); if(x < i) vc[1].pb({x,i}); } if(flag){ int p = -1, mx = -1; for(int i = 0; i < vc[0].size(); ++i){ int l = vc[0][i].fi, r = vc[0][i].se; if(p < l) p = l, mx = a[l]; while(p < r) { ++p; mx = max(mx, a[p]); } if(mx > a[r]){ ans.pb({1,l,r-1}); ans.pb({0,l,r}); } else ans.pb({1,l,r}); mx = a[r]; } p = n+1, mx = -1; for(int i = (int(vc[1].size())) - 1; i >= 0; --i){ int l = vc[1][i].fi, r = vc[1][i].se; if(p > r) p = r, mx = a[r]; while(p > l){ --p; mx = max(mx, a[p]); } if(mx > a[l]){ ans.pb({1,l+1,r}); ans.pb({0,l,r}); } else ans.pb({1,l,r}); mx = a[l]; } printf("%d\n", ans.size()); for(int i = 0; i < ans.size(); ++i){ if(ans[i].op == 1) printf("M "); else printf("m "); printf("%d %d\n", ans[i].l, ans[i].r); } } else puts("-1"); return 0; }
G:Matrix Queries
题意:有一个2^n * 2^n的矩阵, 现在你每次都会修改一列和一行的颜色,现在问你每次修改完之后的整幅图之后的权值是多少。
题解:对于对于每一个2^k(k>1)的矩阵来说, 如果里面的颜色不是同一个颜色, 那么他就会对答案的影响就是加上4,现在只要统计多少个2^k的矩阵是不完全同样的颜色。
然后用线段树去维护关于不考虑列影响, 下对于每一个矩阵来说他的行是不是一样的。不考虑行的影响下每一个矩阵的列是不是一样的。 对于统计出一个矩阵内完全颜色相同下,那么对于这个格子来说他对应的列颜色一样,它对应的行颜色一样。我们算出个数,就可以算出不一样的个数了,然后输出答案。
代码:
#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 = 1 << 22; int cx[N], cy[N], okx[N], oky[N]; int xsz[30], ysz[30]; void buildx(int l, int r, int rt, int k){ okx[rt] = 1; ++xsz[k]; if(l == r) return ; int m = l+r >> 1; buildx(l,m,rt<<1,k-1); buildx(m+1,r,rt<<1|1,k-1); } LL tot = 0, tot2 = 0; void buildy(int l, int r, int rt, int k){ oky[rt] = 1; ++ysz[k]; tot += xsz[k]; tot2 += xsz[k]; if(l == r) return; int m = l+r >> 1; buildy(l, m, rt<<1, k-1); buildy(m+1, r, rt<<1|1, k-1); } void Cx(int L, int l, int r, int rt, int k){ if(l == r){ okx[rt] ^= 1; return ; } int m = l+r >> 1; if(L <= m) Cx(L, lson, k-1); else Cx(L,rson,k-1); if(okx[rt] != -1) tot2 -= ysz[k], --xsz[k]; if(okx[rt<<1] == okx[rt<<1|1]){ okx[rt] = okx[rt<<1]; } else okx[rt] = -1; if(okx[rt] != -1) tot2 += ysz[k], ++xsz[k]; } void Cy(int L, int l, int r, int rt, int k){ if(l == r){ oky[rt] ^= 1; return ; } int m = l+r >> 1; if(L <= m) Cy(L, lson, k-1); else Cy(L,rson,k-1); if(oky[rt] != -1) tot2 -= xsz[k], --ysz[k]; if(oky[rt<<1] == oky[rt<<1|1]) oky[rt] = oky[rt<<1]; else oky[rt] = -1; if(oky[rt] != -1) tot2 += xsz[k], ++ysz[k]; } int main(){ int n, m; scanf("%d%d", &n, &m); int l = 1, r = (1<<n); buildx(1,r,1,n); buildy(1,r,1,n); int op, h; while(m--){ scanf("%d%d", &op, &h); if(op) Cx(h, 1, r, 1, n); else Cy(h, 1, r, 1, n); printf("%lld\n",(tot-tot2)*4+1); } return 0; } /* 2 7 1 3 0 2 1 1 1 4 0 4 0 3 1 1 */
H:Modern Djinn
题意:现在有m条单向边,如果一条单向边成立则需要 u开心 v不开心, 现在需要至少有 m/4+1 边成立, 现在要求你输出成立的方案数。
题解:将每个人的开心值 rand一下, 然后再check答案,知道满足题意,则输出。
代码:
#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 a[N]; pair<int,int> p[N<<1]; int id[N]; int main(){ int T; scanf("%d", &T); while(T--){ int n, m; scanf("%d%d", &n, &m); for(int i = 1; i <= m; ++i) scanf("%d%d", &p[i].fi, &p[i].se); int k = 0, need = m/4 +1 ; do{ for(int i = 1; i <= n; ++i) a[i] = ((int)rand())&1; k = 0; for(int i = 1; i <= m; ++i){ //cout << i << " "<< a[i] << endl; if(a[p[i].fi] == 1 && a[p[i].se] == 0){ id[++k] = i; } } }while(k < need); printf("%d\n", k); for(int i = 1; i <= k; ++i) printf("%d%c", id[i], " \n"[i==k]); } return 0; }
I:Inversion
题解:先把图中给定的信息将原来的1-n的排列找出来,对于样例3来说,原来的序列就是[1,4,2,3],现在考虑这个序列,我们发现如果从1出发,可以往 4, 2的方向走,不可以往3走,因为2-3之间没有边,故如果序列中有3 那么一定有 2 ,我们不能直接跳过2。dp[i]代表以i结尾的方案数,现在我们要继续往后走,我们先走到后面第一个大于a[i]的值,这个点是可以被转移到的,继续往后走,走下坡路,在样例1中就是2,2也是可以通过1转移到的。 我们只要这样暴力转移就好可以得到答案了,为了方便得到最后的答案和开始dp,我们可以从给a[0] = 0, a[n+1] = n+1。故从0号点出发走到n+1号点的方案数就是答案了。
代码:
#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 = 105; int a[N], in[N]; LL dp[N]; int vis[N]; int n, m, u, v; int Find(int x){ x++; for(int i = 1; i <= n; ++i){ if(vis[i]) continue; x--; if(!x) { vis[i] = 1; return i; } } } int main(){ scanf("%d%d", &n, &m); for(int i = 1; i <= m; ++i){ scanf("%d%d", &u, &v); in[min(u,v)]++; } for(int i = 1; i <= n; ++i) a[i] = Find(in[i]); a[0] = 0, a[n+1] = n+1; // for(int i = 1; i <= n; ++i) // cout << i << ' ' << a[i] << endl; dp[0] = 1; for(int i = 0; i <= n; ++i){ int x = a[i]; for(int j = i+1; j <= n+1; ++j){ if(a[j] > a[i]){ dp[j] += dp[i]; // cout << i << " i with j " << j << endl; int mx = a[j]; for(int k = j+1; k <= n+1; ++k){ if(mx > a[k] && a[k] > a[i]){ mx = a[k]; dp[k] += dp[i]; // cout << i << " i with k " << k << endl; } } break; } } } printf("%lld\n", dp[n+1]); return 0; } /* 7 7 5 6 2 3 6 7 2 7 3 1 7 5 7 4 5 7 2 5 1 5 3 5 2 3 4 1 4 3 4 2 5 6 1 3 4 5 1 4 2 3 1 2 1 5 */
J:Rabbit vs Turtle 我不得不说这个题目把我恶心吐了。。。。
题意:龟兔赛跑,一副 n个点m条边的图,现在要乌龟和兔子都有自己的路径,乌龟走完一条边之后他会休息一下,兔子按原来的路走。现在兔子可以作弊,兔子作弊之后乌龟不再睡觉,现在问你兔子在多少个点上作弊能赢。
限制条件: 1. 兔子在某个点作弊的时候,一定是要走从这个点出发的最短路。
2.当兔子作弊的时候,乌龟刚好要入睡,则乌龟先睡觉,再发现兔子作弊。
3.乌龟之后在不睡觉的时候才会发现兔子作弊。
4.兔子新走的路一定要比原来的路径短。
5.当兔子开始作弊的时候,下一个点一定不能是原来的路径上的下一个点。
6.当兔子和乌龟同时走到终点的时候 算兔子胜利。
7.最后的节点要按从小到大的顺序输出。
8.注意这个边是单向边
代码:
#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<LL,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 1e5 + 100; const int M = 2e5 + 100; struct Node{ int u, v, t, r; }e[M]; int head[N], to[M], ct[M], nt[M], tot = 0; void add(int u, int v, int w){ ct[tot] = w; to[tot] = v; nt[tot] = head[u]; head[u] = tot++; } LL dis[N]; int n, m; void dijkstra(){ memset(dis, INF, sizeof dis); priority_queue<pll, vector<pll>, greater<pll> > pq; dis[n] = 0; pq.push(pll(0,n)); while(!pq.empty()){ LL x = pq.top().fi; int u = pq.top().se; pq.pop(); if(dis[u] != x) continue; for(int i = head[u], v; ~i; i = nt[i]){ v = to[i]; if(dis[v] > dis[u] + ct[i]){ dis[v] = dis[u] + ct[i]; pq.push(pll(dis[v],v)); } } } } LL tpre[M], tsuf[M], sum, rpre[N]; int pos[N]; LL Find(int u, int v){ LL ret = INF; for(int i = head[u]; ~i; i = nt[i]){ if(to[i] == v) continue; if(dis[u] == dis[to[i]]+ct[i]) ret = min(ret, ct[i]+dis[to[i]]); } return ret; } int tn, id, rn, sleep; int ans[N], fcnt; void boom(){ for(int i = 0, p = 1; i < rn; ++i){ LL tmp = Find(pos[i], pos[i+1]); if(tmp >= rpre[rn] - rpre[i]) continue; while(p <= 2*tn && tpre[p] < rpre[i]) p++; if(p > 2 * tn) continue; LL t2; if(p&1 && tpre[p] != rpre[i]) t2 = tpre[p] - rpre[i] + tsuf[(p+1)/2+1]; else{ int q = p; if(q&1) q++; t2 = tpre[q] - rpre[i] + tsuf[(q+1)/2+1]; } if(tmp <= t2) ans[++fcnt] = pos[i]; } } int main(){ memset(head, -1, sizeof head); scanf("%d%d", &n, &m); for(int i = 1; i <= m; ++i){ scanf("%d%d%d%d", &e[i].u, &e[i].v, &e[i].t, &e[i].r); add(e[i].v, e[i].u, e[i].r); } dijkstra(); memset(head, -1, sizeof head); tot = 0; for(int i = 1; i <= m; ++i) add(e[i].u, e[i].v, e[i].r); scanf("%d", &tn); for(int i = 1; i <= tn; ++i){ scanf("%d%d", &id, &sleep); tpre[i*2-1] = e[id].t; tpre[i*2] = sleep; } tpre[tn*2] = 0; for(int i = tn; i >= 1; --i) tsuf[i] = tsuf[i+1] + tpre[i*2-1]; for(int i = 1; i <= tn*2; ++i) tpre[i] += tpre[i-1]; scanf("%d", &rn); pos[0] = 1; for(int i = 1; i <= rn; ++i){ scanf("%d", &id); pos[i] = e[id].v; rpre[i] = rpre[i-1] + e[id].r; } boom(); printf("%d\n", fcnt); sort(ans+1, ans+1+fcnt); for(int i = 1; i <= fcnt; ++i) printf("%d%c", ans[i], " \n"[i==fcnt]); return 0; }
K:Points and Rectangles
题解:CDQ。
只要统计出每次添加一个矩形之后前面有多少个点出现在这个矩形内部,添加一个点之后在这个点在多少个矩形内就好了。
对于矩形的询问我们可以通过询问4次差分去做,对于矩形的添加,我们则可以将添加矩形的左边,然后延续长度直到矩形的右边出现,删除这个矩形。
代码:
#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; struct Node{ int op, x1, y1, x2, y2, id; bool operator < (const Node & t) const{ if(x1 != t.x1) return x1 < t.x1; return id < t.id; } }A[N], B[N*4], C[N*4]; int tot = 0, y[N*4]; int ysz = 0; void nownode(int op, int x1, int y1, int id){ ++tot; B[tot].op = op; B[tot].x1 = x1; B[tot].y1 = y1; B[tot].id = id; } LL ans[N]; LL bit[N*4]; void Add(int x, int v){ while(x <= ysz ){ bit[x] += v; x += x & (-x); } } LL Query(int x){ LL ret = 0; while(x > 0){ ret += bit[x]; x -= x & (-x); } return ret; } void CDQ(int l, int r){ if(l == r) return ; int m = l+r >> 1; CDQ(l, m); CDQ(m+1, r); int k = 0; for(int i = l; i <= m; ++i) if(B[i].id == 0) C[++k] = B[i]; for(int i = m+1; i <= r; ++i) if(B[i].id) C[++k] = B[i]; if(k == 0 || C[1].id || !C[k].id) return ; sort(C+1, C+1+k); for(int i = 1; i <= k; ++i){ if(C[i].id == 0) Add(C[i].y1, C[i].op); else ans[C[i].id] += C[i].op * Query(C[i].y1); } for(int i = 1; i <= k; ++i) if(C[i].id == 0) Add(C[i].y1, -C[i].op); } int main(){ int n; scanf("%d", &n); for(int i = 1; i <= n; ++i){ scanf("%d", &A[i].op); scanf("%d%d", &A[i].x1, &A[i].y1); y[++ysz] = A[i].y1; if(A[i].op == 2) { scanf("%d%d", &A[i].x2, &A[i].y2); y[++ysz] = A[i].y2; } } sort(y+1, y+1+ysz); ysz = unique(y+1, y+1+ysz) - y - 1; for(int i = 1; i <= n; i++){ if(A[i].op == 1) { A[i].y1 = lower_bound(y+1, y+1+ysz, A[i].y1) - y; nownode(1, A[i].x1, A[i].y1, 0); } else{ A[i].y1 = lower_bound(y+1, y+1+ysz, A[i].y1) - y; A[i].y2 = lower_bound(y+1, y+1+ysz, A[i].y2) - y; nownode(1, A[i].x2, A[i].y2, i); nownode(1, A[i].x1-1, A[i].y1-1, i); nownode(-1, A[i].x1-1, A[i].y2, i); nownode(-1, A[i].x2, A[i].y1-1, i); } } CDQ(1, tot); tot = 0; for(int i = 1; i <= n; ++i){ if(A[i].op == 1){ nownode(1, A[i].x1, A[i].y1, i); } else { nownode(1, A[i].x1, A[i].y1, 0); nownode(-1, A[i].x1, A[i].y2+1, 0); nownode(-1, A[i].x2+1, A[i].y1, 0); nownode(1, A[i].x2+1, A[i].y2+1,0); } } CDQ(1, tot); for(int i = 1; i <= n; ++i){ ans[i] += ans[i-1]; printf("%lld\n", ans[i]); } return 0; }