18.8.24 考试总结
一开始我这道题竟然以为是最小费用最大流...怪我错误的预估了网络流的复杂度...。
然后把1mb记成了1e7...我已经被自己蠢死了cnm
这道题就是一个二分 + 贪心 先把所有的人和所有的钥匙从左到右排一边序
二分最大的限制时间是多少 然后就O(n)check是否合法
现在的问题就是如何check 那么对于每一把钥匙c 两个人a b
如果a拿钥匙合法 b拿钥匙则不一定合法 会更劣
所以从左往右扫的时候如果这个人能拿这个钥匙就拿 不能拿钥匙就换向右的下一把 看最终合法的人数是否等于n就可以了
代码
#include <bits/stdc++.h> #define oo 1e9 using namespace std; const int N = 1e5 + 5; int n,m,ans = oo,k,b[N],a[N]; bool check(int lim) { int key = 1,p = 1; int num = 0; while(key <= m) { if(abs(k - b[key]) + abs(a[p] - b[key]) <= lim) p ++,num ++; if(num == n) break; key ++; } return num == n; } void solve( ) { int l = 1,r = oo; ans = oo; while(l <= r) { int mid = (l + r) >> 1; if(check(mid)) ans = mid,r = mid - 1; else l = mid + 1; } } int main( ) { freopen("keys.in","r",stdin); freopen("keys.out","w",stdout); scanf("%d%d%d",& n,& m,& k); for(int i = 1;i <= n;i ++) scanf("%d",& a[i]); for(int i = 1;i <= m;i ++) scanf("%d",& b[i]); sort(a + 1,a + n + 1); sort(b + 1,b + m + 1); solve( ); printf("%d",ans); }
这道题就是一道裸的数据结构吧我觉得...
考试的时候还是调了一段时间
就线段树各种操作都搞一搞就可以了..。
我的query比较奇怪 就是乱搞搞某个区间的size 以及最小值的pos
代码
#include <bits/stdc++.h> #define oo 1e9 using namespace std; typedef long long ll; const int N = 1e5 + 5; int n,a[N],nex[N],las[N],now; ll ans; struct node { int val,size,pos; }f[4 * N]; inline int read( ) { int ans = 0,t = 1; char x; x = getchar( ); while(x < '0' || x > '9') { if(x == '-') t = -1; x = getchar( ); } while(x >= '0' && x <= '9') { ans = ans * 10 + x - '0'; x = getchar( ); } return ans * t; } void update(int o) { f[o].val = min(f[2 * o].val,f[2 * o + 1].val); f[o].size = f[2 * o].size + f[2 * o + 1].size; f[o].pos = f[2 * o].val <= f[2 * o + 1].val ? f[2 * o].pos : f[2 * o + 1].pos; } void build(int o,int l,int r) { if(l == r) { f[o].val = a[l]; f[o].pos = l; f[o].size = 1; return ; } int mid = (l + r) >> 1; build(2 * o,l,mid); build(2 * o + 1,mid + 1,r); update(o); } void modify(int o,int l,int r,int p) { if(l == r) { f[o].val = oo; f[o].pos = 0; f[o].size = 0; return ; } int mid = (l + r) >> 1; if(p <= mid) modify(2 * o,l,mid,p); else modify(2 * o + 1,mid + 1,r,p); update(o); } node query(int o,int l,int r,int L,int R) { if(l >= L && r <= R) { return f[o]; } int mid = (l + r) >> 1; node ans; ans.val = oo,ans.pos = 0,ans.size = 0; if(L <= mid) { node ll; ll = query(2 * o,l,mid,L,R); ans.val = min(ans.val,ll.val); ans.pos = ll.pos; ans.size += ll.size; } if(mid < R) { node rr; rr = query(2 * o + 1,mid + 1,r,L,R); if(ans.val > rr.val) ans.pos = rr.pos; ans.val = min(ans.val,rr.val); ans.size += rr.size; } return ans; } void solve( ) { now = 1; for(int i = 1;i <= n;i ++) { nex[i] = i + 1; if(i == n) nex[i] = 1; las[i] = i - 1; if(i == 1) las[i] = n; } int p; for(int i = 1;i <= n;i ++) { node np,qp; qp = query(1,1,n,now,n); if(now > 1){ np = query(1,1,n,1,now - 1); } else np.val = oo,np.size = 0; node ss = query(1,1,n,1,now); if(qp.val > np.val) { p = np.pos; node s = query(1,1,n,1,np.pos); ans += (ll)f[1].size - (ll)(ss.size - s.size - 1); } else { p = qp.pos; node s = query(1,1,n,1,qp.pos); ans += (ll)(s.size - ss.size + 1); } modify(1,1,n,p); int pre = las[p]; now = nex[p]; nex[pre] = now; las[now] = pre; } } int main( ) { freopen("cards.in","r",stdin); freopen("cards.out","w",stdout); n = read( ); for(int i = 1;i <= n;i ++) a[i] = read( ); build(1,1,n); solve( ); printf("%I64d",ans); }
第三题是一道整除分块的题
由题目可得 ∑(ai / d)上取整 * d - ai ≤ k
昨天idy给我们讲了如何推出满足a / d 下取整= x的最大d
同样的 今天这道题是求得满足a / d上取整 = 某个值的最小d
所以就对d进行分块 讲a / d相等的分为一个快 因为这样子同一个块内d是满足二分性的
但是因为有多个a 所以就对所有小断点都分一下块
就像这样
可以进行二分来找最大的满足条件的d 因为将式子里的d提出来后 同一个块内的∑(ai / d)上取整是相同的
所以这个东西就变成了一个关于d的一次函数 就可以二分 二分朴素做法是O(n)判断 这道题是可以的
但是有更优秀的O(1)判断 对于同一个块内 d的增减只会改变系数的个数 而系数是不变的 所以可以直接加上delta d * 系数
这样二分check时 复杂度就从n变成了1 (然而标程写的是O(n)check)
代码
#include <bits/stdc++.h> using namespace std; const int N = 111; int n; long long k; vector<long long> vc; long long aa[N]; inline long long read( ) { long long ans = 0,t = 1; char x; x = getchar( ); while(x < '0' || x > '9') { if(x == '-') t = -1; x = getchar( ); } while(x >= '0' && x <= '9') { ans = ans * 10 + x - '0'; x = getchar( ); } return ans * t; } void split( long long a, vector<long long> &vc ) { vc.push_back(a); for(long long d = a;d;) { long long dd = (a + d - 1) / d; long long k = (a + dd - 1) / dd; vc.push_back(k); d = k - 1; } } bool check( long long d ) { long long sum = 0; for( int i = 1; i <= n; i++ ) { sum += (aa[i] + d - 1) / d * d - aa[i]; } return sum <= k; } int main() { freopen("bamboo.in", "r", stdin); freopen("bamboo.out", "w", stdout); scanf("%d%I64d", &n, &k ); for( int i = 1; i <= n; i++ ) { aa[i] = read( ); split(aa[i],vc); } sort( vc.begin(), vc.end() ); vc.erase(unique(vc.begin(),vc.end()),vc.end()); vc.push_back( 10000000000000ll ); long long ans = 1; for( int t = 0; t + 1 < (int)vc.size(); t++ ) { long long lf = vc[t]; long long rg = vc[t+1] - 1; if( check(lf) == false ) continue; while( lf < rg ) { long long mid = (lf + rg + 1) >> 1; if( check(mid) ) lf = mid; else rg = mid - 1; } ans = max( ans, lf ); } printf( "%I64d\n", ans ); }