zoukankan      html  css  js  c++  java
  • Codeforces Round #466 (Div. 2) 题解

    人生中第三次(CF)。。。
    考试中切了(A)~(E)
    (F)题会做没时间写


    题解

    A:Points on the line

    题意

    给定一个数列,删最小的数,使最大差不大于一个定值

    Sol

    排序后选的一定是段连续的区间,枚举左右端点即可

    手速慢了233

    # include <bits/stdc++.h>
    # define RG register
    # define IL inline
    # define Fill(a, b) memset(a, b, sizeof(a))
    using namespace std;
    typedef long long ll;
    const int _(105);
    
    IL int Input(){
        RG int x = 0, z = 1; RG char c = getchar();
        for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
        return x * z;
    }
    
    int n, d, x[_], ans;
    
    int main(RG int argc, RG char* argv[]){
    	n = Input(), d = Input();
    	for(RG int i = 1; i <= n; ++i) x[i] = Input();
    	sort(x + 1, x + n + 1);
    	if(!n || x[n] - x[1] <= d) return puts("0"), 0;
    	for(RG int i = 1; i <= n; ++i)
    		for(RG int j = i; j <= n; ++j)
    			if(x[j] - x[i] <= d) ans = max(ans, j - i + 1);
    	printf("%d
    ", n - ans);
    	return 0;
    }
    

    B:Our Tanya is Crying Out Loud

    题意

    给定数(n)(k)以及代价(A)(B)
    你可以花(A)的代价给(n)(1)
    或者当(n)(k)的倍数时花(B)的代价把(n)变为(n/k)
    (n)变为(1)的最小代价

    Sol

    不是(k)的倍数,就减到(k)的倍数为止
    否则比较减和除的代价,取最小

    # include <bits/stdc++.h>
    # define RG register
    # define IL inline
    # define Fill(a, b) memset(a, b, sizeof(a))
    using namespace std;
    typedef long long ll;
    const int _(105);
    
    IL int Input(){
        RG int x = 0, z = 1; RG char c = getchar();
        for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
        return x * z;
    }
    
    int n, k, a, b;
    ll ans = 0;
    
    int main(RG int argc, RG char* argv[]){
    	n = Input(), k = Input(), a = Input(), b = Input();
    	if(k == 1){
    		cout << 1LL * a * (n - 1) << endl;
    		return 0;
    	}
    	while(n >= k){
    		RG int t = n / k, tt = n % k;
    		n -= tt;
    		ans += 1LL * tt * a;
    		ans += min(1LL * b, 1LL * (n - t) * a);
    		n = t;
    	}
    	ans += 1LL * a * (n - 1);
    	cout << ans << endl;
    	return 0;
    }
    

    C:Phone Numbers

    题意

    一个长度为(n)的小写字母组成的字符串和一个整数(k),要你生成一个最小字典序的长度为(k)的小写字母组成的字符串
    使该字符串的字符集为给定串的字符集的子集,并且字典序大于给定串

    Sol

    找到一个最后面的位置可以使答案串大于原串,后面直接填最小的字符

    # include <bits/stdc++.h>
    # define RG register
    # define IL inline
    # define Fill(a, b) memset(a, b, sizeof(a))
    using namespace std;
    typedef long long ll;
    const int _(1e5 + 5);
    
    IL int Input(){
        RG int x = 0, z = 1; RG char c = getchar();
        for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
        return x * z;
    }
    
    int n, k, t[30], pos, g[30];
    char s[_];
    
    int main(RG int argc, RG char* argv[]){
    	n = Input(), k = Input();
    	scanf(" %s", s + 1), s[n + 1] = 'a' - 1;
    	for(RG int i = 1; i <= n; ++i) ++t[s[i] - 'a'];
    	for(RG int i = 25; ~i; --i) g[i] = t[i], t[i] += t[i + 1];
    	for(RG int i = min(k, n + 1); i; --i)
    		if(t[s[i] - 'a' + 1]){
    			pos = i;
    			break;
    		}
    	for(RG int i = 1; i < pos; ++i) printf("%c", s[i]);
    	for(RG int i = s[pos] - 'a' + 1; i < 26; ++i)
    		if(g[i]){
    			printf("%c", i + 'a');
    			break;
    		}
    	RG int tmp = 0;
    	for(RG int i = 0; i < 26; ++i)
    		if(g[i]){
    			tmp = i;
    			break;
    		}
    	for(RG int i = pos + 1; i <= k; ++i) printf("%c", tmp + 'a');
    	return 0;
    }
    

    D:Alena And The Heater

    题意

    给定数列(A)
    以及生成数列(B)的条件:
    (B[1]=B[2]=B[3]=B[4]=0)
    对于(i>4)
    如果
    (a[i],a[i-1],a[i-2],a[i-3],a[i-4]>r)
    (b[i-1]=b[i-2]=b[i-3]=b[i-4]=1)
    (b[i]=1)
    如果
    (a[i],a[i-1],a[i-2],a[i-3],a[i-4]<l)
    (b[i-1]=b[i-2]=b[i-3]=b[i-4]=0)
    (b[i]=0)
    否则(b[i]=b[i-1])
    保证一定有解,输出(l, r)可以构造出给定的数列(B)
    输出任意一组(l,rin[-1e9, 1e9])

    Sol

    保证一定有解,那么按题意反过来构造(l, r)即可

    # include <bits/stdc++.h>
    # define RG register
    # define IL inline
    # define Fill(a, b) memset(a, b, sizeof(a))
    using namespace std;
    typedef long long ll;
    const int _(1e5 + 5);
    
    IL int Input(){
        RG int x = 0, z = 1; RG char c = getchar();
        for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
        return x * z;
    }
    
    int n, a[_], b[_], l = -1e9, r = 1e9;
    char s[_];
    
    int main(RG int argc, RG char* argv[]){
    	n = Input();
    	for(RG int i = 1; i <= n; ++i) a[i] = Input();
    	scanf(" %s", s + 1);
    	for(RG int i = 1; i <= n; ++i) b[i] = s[i] - '0';
    	for(RG int i = 5; i <= n; ++i){
    		RG int s = b[i - 1] + b[i - 2] + b[i - 3] + b[i - 4];
    		if(s && s != 4) continue;
    		if(b[i] == b[i - 1]) continue;
    		if(b[i]){
    			RG int mx = -1e9;
    			for(RG int j = 0; j <= 4; ++j) mx = max(mx, a[i - j]);
    			l = max(l, mx + 1);
    		}
    		else{
    			RG int mn = 1e9;
    			for(RG int j = 0; j <= 4; ++j) mn = min(mn, a[i - j]);
    			r = min(r, mn - 1);
    		}
    	}
    	printf("%d %d
    " ,l, r);
    	return 0;
    }
    

    E:Cashback

    题意

    给定一个长度为(n)的数列以及一个整数(c)
    一个下标在([l, r])内的子数列的价值为所有数的和减去子数列中前(lfloorfrac{r-l+1}{c} floor)小的数的和
    求把这个数列分成若干个块,使的每块的价值和最小

    Sol

    (k)小?离散化+主席树辣
    码完
    发现只会(n^2)的滴劈
    (f[i])表示做到第(i)个数的最小代价,枚举长度转移,前(k)小的和主席树查询
    你当这是(OI)赛制啊?没有部分分的。。
    我们分情况考虑

    • 若分一块长度小于(c)
      价值就是所有的数的和
      那么还不如一个一个选更方便转移
    • 若分一块长度为(c)的倍数的
      我们把它分成若干个长度为(c)的块,就是总和减去每个块的最小值的和
      而直接分一块就是总和减去整个块的前几个最小值
      整个块的前几个最小值一定小于等于若干块的最小值和
      那么分成若干个长度为(c)的块最优
    • 若一块长度大于等于(c)的,就是上面两种组合而来

    那么策略就是:要么一个一个选,要么选(c)个一块
    只要求区间最小值,(RMQ)问题
    然而主席树懒得删

    # include <bits/stdc++.h>
    # define RG register
    # define IL inline
    # define Fill(a, b) memset(a, b, sizeof(a))
    using namespace std;
    typedef long long ll;
    const int _(1e5 + 5);
    
    IL int Input(){
        RG int x = 0, z = 1; RG char c = getchar();
        for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
        return x * z;
    }
    
    int n, c, tot, len, rt[_];
    ll f[_], s[_], o[_], a[_];
    struct HJT{
    	int sz, rs, ls;
    	ll sum;
    } T[_ * 20];
    
    IL void Modify(RG int &x, RG int l, RG int r, RG int id, RG ll v){
    	T[++tot] = T[x], T[x = tot].sum += v, ++T[x].sz;
    	if(l == r) return;
    	RG int mid = (l + r) >> 1;
    	if(id <= mid) Modify(T[x].ls, l, mid, id, v);
    	else Modify(T[x].rs, mid + 1, r, id, v);
    }
    
    IL ll Query(RG int A, RG int B, RG int l, RG int r, RG int k){
    	if(l == r) return o[l];
    	RG int mid = (l + r) >> 1, ss = T[T[B].ls].sz - T[T[A].ls].sz;
    	RG ll sss = T[T[B].ls].sum - T[T[A].ls].sum;
    	if(ss >= k) return Query(T[A].ls, T[B].ls, l, mid, k);
    	else return sss + Query(T[A].rs, T[B].rs, mid + 1, r, k - ss);
    }
    
    int main(RG int argc, RG char* argv[]){
    	n = Input(), c = Input(), Fill(f, 127), f[0] = 0;
    	for(RG int i = 1; i <= n; ++i) o[i] = a[i] = Input(), s[i] = s[i - 1] + a[i];
    	sort(o + 1, o + n + 1), len = unique(o + 1, o + n + 1) - o - 1;
    	for(RG int i = 1; i <= n; ++i){
    		RG int p = lower_bound(o + 1, o + len + 1, a[i]) - o;
    		rt[i] = rt[i - 1], Modify(rt[i], 1, len, p, a[i]);
    	}
    	for(RG int i = 1; i < c; ++i) f[i] = s[i];
    	for(RG int i = c; i <= n; ++i){
    		RG ll sum = s[i] - s[i - c] - Query(rt[i - c], rt[i], 1, len, 1);
    		f[i] = min(f[i - 1] + a[i], f[i - c] + sum);
    	}
    	cout << f[n] << endl;
    	return 0;
    }
    

    F:Machine Learning

    题意

    给一个数列
    每次询问一个区间([l,r])
    求数列下标在这个区间内的数的个数的(mex)
    (p.s)(mex)值是最小的没出现的整数值
    数会随时修改

    Sol

    这不就是离散化+带修改莫队吗?
    吐槽一下:
    (mex)值开桶暴力求就能过了

    我的做法:
    把数字也分块,计算(mex)时,找到第一个不满的块,再在块内找的那个(mex)

    还有一点就是
    (TM)学了假的带修改莫队:
    没按时间为第三关键字排序!!!
    没把块的大小设为(n^frac{2}{3})!!!
    没比较右端点所在的块!!!
    然后一直(TLE)(QAQ)

    # include <bits/stdc++.h>
    # define RG register
    # define IL inline
    # define Fill(a, b) memset(a, b, sizeof(a))
    using namespace std;
    typedef long long ll;
    const int _(4e5 + 5);
    
    IL int Input(){
        RG int x = 0, z = 1; RG char c = getchar();
        for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
        return x * z;
    }
    
    int n, q, bl[_], o[_], len, a[_], ans[_], blo, cnt, tot;
    int t[_], sum[5000], size[_];
    struct Query{
    	int l, r, t, id;
    
    	IL bool operator <(RG Query B) const{
    		if(bl[l] != bl[B.l]) return l < B.l;
    		if(bl[r] != bl[B.r]) return r < B.r;
    		return t < B.t;
    	}
    } qry[_];
    struct Modify{
    	int p, x;
    } mdy[_];
    
    IL void Change(RG int x, RG int d){
    	if(d > 0){
    		if(!size[x]) ++sum[x / blo];
    		++size[x];
    	}
    	else{
    		--size[x];
    		if(!size[x]) --sum[x / blo];
    	}
    }
    
    IL int Mex(){
    	for(RG int i = 0; ; ++i)
    		if(sum[i] != blo){
    			for(RG int j = 0; j < blo; ++j)
    				if(!size[i * blo + j]) return i * blo + j;
    		}
    }
    
    IL void Calc(RG int v, RG int d){
    	Change(t[v], -1), t[v] += d, Change(t[v], 1);
    }
    
    IL void Adjust(RG int j, RG int l, RG int r){
    	if(mdy[j].p >= l && mdy[j].p <= r) Calc(a[mdy[j].p], -1);
    	swap(a[mdy[j].p], mdy[j].x);
    	if(mdy[j].p >= l && mdy[j].p <= r) Calc(a[mdy[j].p], 1);
    }
    
    int main(RG int argc, RG char* argv[]){
    	n = Input(), q = Input(), blo = pow(n, 2.0 / 3.0);
    	for(RG int i = 1; i <= n; ++i) o[++len] = a[i] = Input(), bl[i] = (i - 1) / blo;
    	for(RG int i = 1; i <= q; ++i){
    		RG int op = Input(), x = Input(), y = Input();
    		if(op == 1) qry[++cnt] = (Query){x, y, tot, cnt};
    		else mdy[++tot] = (Modify){x, y}, o[++len] = y;
    	}
    	sort(o + 1, o + len + 1), len = unique(o + 1, o + len + 1) - o - 1;
    	for(RG int i = 1; i <= n; ++i)
    		a[i] = lower_bound(o + 1, o + len + 1, a[i]) - o;
    	for(RG int i = 1; i <= tot; ++i)
    		mdy[i].x = lower_bound(o + 1, o + len + 1, mdy[i].x) - o;
    	sort(qry + 1, qry + cnt + 1), blo = sqrt(len);
    	for(RG int L = qry[1].l, R = qry[1].l - 1, i = 1, j = 0; i <= cnt; ++i){
    		while(L < qry[i].l) Calc(a[L++], -1);
    		while(L > qry[i].l) Calc(a[--L], 1);
    		while(R < qry[i].r) Calc(a[++R], 1);
    		while(R > qry[i].r) Calc(a[R--], -1);
    		while(j < qry[i].t) Adjust(++j, L, R);
    		while(j > qry[i].t) Adjust(j--, L, R);
    		ans[qry[i].id] = Mex();
    	}
    	for(RG int i = 1; i <= cnt; ++i) printf("%d
    ", ans[i]);
    	return 0;
    }
    
    

    总结

    (CF)比赛的题还是不错的
    总有一些奇奇怪怪的脑洞
    话说歪果仁知道主席树和莫队算法吗?

  • 相关阅读:
    weekly review 200836: the MidAutumn Festival
    weekly review 200841: Good Weekend
    解决ListView的onitemclick事件无法响应
    Android退出程序时的"再按一次退出"实现
    Android开发之文件下载
    android ExpandableListView详解
    Android ListView及其属性
    android 图片放大的处理
    android ExpandableListView
    Toast.makeText用法
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/8468558.html
Copyright © 2011-2022 走看看