data structure
这次总的来说有些懵逼,不知道系统,做出来的也有很大一部分乱搞的成分,赛后补题学到很多。
erase为O(n)的复杂度。
根据一个条件排序后,二分查找满足另一个条件。
区间过渡
莫队算法
差分
线段树,树状数组
A. Cells Not Under Attack
每次放棋子时,检查棋子所在的行和列是否已经有棋子,若没有,则将未受威胁的格子总数减去(行数+列数-1),若行或列上已经有棋子,则只减去行数或列数,分类讨论即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <iostream> #include <queue> #include <algorithm> using namespace std;#define ll long long const int maxn = 1e5 + 5 ;vector<int >x; vector<int >y; bool inx (int xi) { return binary_search (x.begin (), x.end (), xi); } bool iny (int yi) { return binary_search (y.begin (), y.end (), yi); } bool usedx[maxn];bool usedy[maxn];int numx, numy;int main () { ll n, m; cin >> n >> m; long long T = n*n; for (int i = 0 ; i < m; i++) { int xi, yi; ll to_minus = 0 ; cin >> yi >> xi; if (T == 0 ) { cout << 0 <<' ' ; continue ; } if ((!usedx[xi]) && (!usedy[yi])) { to_minus = n + n - 1 - numx - numy; numx++; numy++; usedx[xi] = 1 ; usedy[yi] = 1 ; } else if ((!usedx[xi]) && usedy[yi]) { to_minus = n - numy; numx++; usedx[xi] = 1 ; } else if (usedx[xi] && (!usedy[yi])) { to_minus = n - numx; numy++; usedy[yi] = 1 ; } else if (usedx[xi] && usedy[yi]) to_minus = 0 ; T -= to_minus; if (T < 0 ) T = 0 ; cout << T << ' ' ; } return 0 ; }
B. Too Easy Problems
由于本题只需要输出满足条件的一种答案,所以假设做的题目数等于最终得分数,即只做能得分的题目。
将所有题目按照耗时从小到大排序,对要做的题目数二分查找,确定最终要做多少题目,确定题数之后,从头到尾确定做哪些题目,由于题目耗时从小到大排序,所以最后答案一定是最优。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <iostream> #include <algorithm> using namespace std;const int maxn = 2e5 + 5 ;int T, n, st, ed, times, cnt;struct Node { int most, time, index; }nd[maxn]; bool cmp (Node a, Node b) { return a.time < b.time; } int main () { ios::sync_with_stdio (false ); scanf ("%d%d" , &n, &T); for (int i = 1 ; i <= n; i++) { scanf ("%d%d" , &nd[i].most, &nd[i].time); nd[i].index = i; } sort (nd + 1 , nd + n + 1 , cmp); st = 0 , ed = n; while (st < ed) { int md = (ed + st + 1 ) >> 1 ; cnt = 0 , times = 0 ; for (int i = 1 ; i <= n; i++) { if (nd[i].most >= md) { if (times + nd[i].time <= T) { times += nd[i].time, cnt++; } else break ; } } if (cnt >= md) { st = md; } else ed = md - 1 ; } printf ("%d\n%d\n" , st, st); cnt = 0 ; for (int i = 1 ; i <= n&&cnt < st; i++) { if (nd[i].most >= st) { printf ("%d " ,nd[i].index); cnt++; } } return 0 ; }
C. Minimum Array
先利用lower_bound找到大于等于n − a i n-a_i n − a i 的数,并erase掉。
若未找到,则从头开始找到第一个不为0的数。指向开头的指针+1。
当所有元素b中都相等时,erase耗时太大,所以要选第一个。
注意先输出,再erase,否则会越界!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> #include <algorithm> #include <vector> using namespace std;#define ll long long const int maxn = 2e5 + 5 ;int a[maxn];vector<int >vc; int main () { std::ios::sync_with_stdio (false ); int n; cin >> n; for (int i = 0 ; i < n; i++) { cin >> a[i]; } int st = 0 ; for (int i = 0 ; i < n; i++) { int tp; cin >> tp; vc.push_back (tp); } sort (vc.begin (), vc.end ()); for (int i = 0 ; i < n; i++) { int best = n - a[i]; int pos = lower_bound (vc.begin () + st, vc.end (), best) - vc.begin (); if (pos == vc.size () || vc[pos] == vc[0 ]) { cout << (vc[st] + a[i]) % n<<' ' ; st++; continue ; } if ((a[i] + vc[st] % n) < (vc[pos] + a[i]) % n) { cout << (a[i] + vc[st]) % n << ' ' ; st++; } else { cout << (a[i] + vc[pos]) % n << ' ' ; vc.erase (vc.begin () + pos); } } return 0 ; }
D. Powerful array
裸的普通莫队
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <iostream> #include <algorithm> #include <cmath> using namespace std;#define ll long long const int maxn = 2e5 + 50 ;int n, t;int a[maxn];int unit;ll ans; int cnt[1000005 ];ll res[maxn]; struct Node { int st, ed; int idt; }que[maxn]; bool cmp (Node a, Node b) { return (a.st / unit == b.st / unit) ? (a.ed < b.ed) : (a.st / unit < b.st / unit); } void add (int x) { ans += ((cnt[a[x]] << 1 ) + 1 )*a[x]; cnt[a[x]]++; } void del (int x) { ans -= ((cnt[a[x]] << 1 ) - 1 )*a[x]; cnt[a[x]]--; } int main () { ios::sync_with_stdio (false ); cin >> n >> t; unit = sqrt (t); for (int i = 1 ; i <= n; i++) { cin >> a[i]; } for (int i = 1 ; i <= t; i++) { cin >> que[i].st >> que[i].ed; que[i].idt = i; } sort (que + 1 , que + t + 1 , cmp); int L = 0 , R = 0 ; for (int i = 1 ; i <= t; i++) { while (L < que[i].st)del (L++); while (L > que[i].st)add (--L); while (R > que[i].ed)del (R--); while (R < que[i].ed)add (++R); res[que[i].idt] = ans; } for (int i = 1 ; i <= t; i++) { cout << res[i] << endl; } return 0 ; }
E. Interesting Array
线段树,差分
先根据所有条件构造答案数组,再用答案数组比对每一个询问,若有不符合的,输出NO。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 #include <iostream> using namespace std;#define lson l,mid,2*id #define rson mid+1,r,2*id+1 const int maxn = 150000 ;struct Node { int l, r, val; }que[maxn]; int INF ;int tree[maxn*4 ];int a[maxn]; int n, m;int sum[32 ][maxn];int tot[32 ];void push_up (int id) { tree[id] = tree[2 * id] & tree[2 * id + 1 ]; } void build (int l, int r, int id) { if (l == r) { tree[id] = a[l]; return ; } int mid = (l + r) >> 1 ; build (lson); build (rson); push_up (id); } int query (int L,int R,int l,int r,int id) { if (L <= l && R >= r) { return tree[id]; } int ans = INF; int mid = (l+r) >> 1 ; if (L <= mid)ans&=query (L, R, lson); if (R > mid)ans&=query (L, R, rson); return ans; } int main () { ios::sync_with_stdio (false ); for (int i = 0 ; i <= 30 ; i++)INF += (1 << i); cin >> n >> m; for (int i = 1 ; i <= m; i++) { int L, R, val; cin >> L >> R >> val; que[i].l = L; que[i].r = R, que[i].val = val; for (int j = 0 ; j <= 30 ; j++) { if ((val&((1 << j))) > 0 ) { sum[j][L]++; sum[j][R + 1 ]--; } } } for (int i = 1 ; i <= n; i++) { int now = 0 ; for (int j = 0 ; j < 30 ; j++) { tot[j] += sum[j][i]; } for (int j = 0 ; j < 30 ; j++) { if (tot[j] > 0 )now += (1 << j); } a[i] = now; } build (1 , n, 1 ); for (int i = 1 ; i <= m; i++) { int val = que[i].val; int L = que[i].l, R = que[i].r; if (query (L, R, 1 , n, 1 ) != val) { cout << "NO" ; return 0 ; } } cout << "YES" << endl; for (int i = 1 ; i <= n; i++) { cout << a[i] << ' ' ; } return 0 ; }
H. Subsegments
要求只出现了一次且最大的数,从第一个长度为k的区间开始,记录区间中每个元素的出现次数,过渡到下个区间只要考虑减去的数于新增的数的出现次数,使用set自动排序存一段区间中只出现了一次的数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <iostream> #include <set> #include <map> #include <algorithm> using namespace std;const int maxn = 1e5 + 5 ;int a[maxn];set<int >st; map<int , int >mp; int main () { int n,k; cin >> n >> k; for (int i = 0 ; i < n; i++) { cin >> a[i]; } for (int i = 0 ; i < k; i++) { mp[a[i]]++; if (mp[a[i]] == 1 )st.insert (a[i]); else if (mp[a[i]] == 2 )st.erase (a[i]); } if (st.size () != 0 )cout << *(--st.end ()) << endl; else cout << "Nothing" << endl; for (int i = k; i < n ; i++) { mp[a[i]]++; if (mp[a[i]] == 1 )st.insert (a[i]); else if (mp[a[i]] == 2 )st.erase (a[i]); mp[a[i - k]]--; if (mp[a[i - k]] == 1 )st.insert (a[i - k]); else if (mp[a[i - k]] ==0 )st.erase (a[i - k]); if (st.size () != 0 )cout << *(--st.end ()) << endl; else cout << "Nothing" << endl; } return 0 ; }
I. Little Girl and Maximum Sum
差分+前缀和, 记录每个位置的出现次数,排序,将原数组排序,次数多的配上数字大的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <iostream> #include <algorithm> #include <vector> using namespace std;const int maxn = 2e5 + 5 ;#define ll long long typedef pair<int , int > pii;ll a[maxn]; vector<pii>vc; ll times[maxn]; ll rt[maxn]; int main () { ll n, q; cin >> n >> q; for (int i = 1 ; i <= n; i++) { cin >> a[i]; } for (int i = 0 ; i < q; i++) { int m, n; cin >> m >> n; times[m]++; times[n + 1 ]--; } for (int i = 1 ; i <= n; i++) { rt[i] = rt[i - 1 ] + times[i]; } sort (a+1 , a + n+1 ); sort (rt + 1 , rt + n + 1 ); ll ans=0 ; for (int i = 1 ; i <=n; i++) { ans += a[i] * rt[i]; } cout << ans; return 0 ; }
K. Queries about less or equal elements
离线询问,先排序,对a,b都要从小到大排序,对于每一次询问,缩小在a中的搜索范围。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> #include <algorithm> #include <vector> using namespace std;const int maxn = 2 * 1e5 + 5 ;typedef pair<int , int > pii;vector<int >a; vector<pii>b; int cnt[maxn];bool cmp (pii a,pii b) { return a.first < b.first; } int main () { int m, n; cin >> m >> n; for (int i = 0 ; i < m; i++) { int tp; cin >> tp; a.push_back (tp); } for (int i = 0 ; i < n; i++) { int tp; cin >> tp; b.push_back (pii (tp, i)); } sort (a.begin (), a.end ()); sort (b.begin (), b.end (), cmp); int st = 0 ; for (int i = 0 ; i < n; i++){ int ans = upper_bound (a.begin () + st, a.end (), b[i].first) - a.begin (); cnt[b[i].second] = ans; st=ans; } for (int i = 0 ; i < n; i++) { cout << cnt[i] << ' ' ; } return 0 ; }