zoukankan      html  css  js  c++  java
  • 【AtCoder】ARC098题解

    C - Attention

    枚举,计算前缀和即可

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define space putchar(' ')
    #define enter putchar('
    ')
    #define mp make_pair
    #define pb push_back
    #define MAXN 300005
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    template<class T>
    void read(T &res) {
    	res = 0;T f = 1;char c = getchar();
    	while(c < '0' || c > '9') {
    		if(c == '-') f = -1;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9') {
    		res = res * 10 + c - '0';
    		c = getchar();
    	}
    	res *= f;
    }
    template<class T>
    void out(T x) {
    	if(x < 0){putchar('-');x = -x;}
    	if(x >= 10) out(x / 10);
    	putchar('0' + x % 10);
    }
    
    int N,sum[2][MAXN];
    char s[MAXN];
    void Solve() {
    	read(N);
    	scanf("%s",s + 1);
    	for(int i = 1 ; i <= N ; ++i) {
    		sum[0][i] = sum[0][i - 1];sum[1][i] = sum[1][i - 1];
    		if(s[i] == 'E') sum[0][i]++;
    		else sum[1][i]++;
    	}
    	int ans = N;
    	for(int i = 1 ; i <= N ; ++i) {
    		ans = min(i - 1 - sum[0][i - 1] + (N - i - (sum[1][N] - sum[1][i])),ans);
    	}
    	out(ans);enter;
    }
    int main() {
    #ifdef ivorysi
    	freopen("f1.in","r",stdin);
    #endif
    	Solve();
    }
    

    D - Xor Sum 2

    等价与一个区间每一数位只能有一个1

    我们记录(nxt[i][j])表示第(i)位数下一个第(j)位是1的位置是什么

    我们枚举左端点,然后去计算右端点,取最小的右端点

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define space putchar(' ')
    #define enter putchar('
    ')
    #define mp make_pair
    #define pb push_back
    #define MAXN 200005
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    template<class T>
    void read(T &res) {
    	res = 0;T f = 1;char c = getchar();
    	while(c < '0' || c > '9') {
    		if(c == '-') f = -1;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9') {
    		res = res * 10 + c - '0';
    		c = getchar();
    	}
    	res *= f;
    }
    template<class T>
    void out(T x) {
    	if(x < 0){putchar('-');x = -x;}
    	if(x >= 10) out(x / 10);
    	putchar('0' + x % 10);
    }
    
    int N,a[MAXN],nxt[MAXN][25];
    void Solve() {
    	read(N);
    	for(int i = 1 ; i <= N ; ++i) read(a[i]);
    	for(int i = 0 ; i < 20 ; ++i) nxt[N + 1][i] = N + 1;
    
    	for(int i = N ; i >= 1 ; --i) {
    		for(int j = 0 ; j < 20 ; ++j) {
    			if(a[i] >> j & 1) {
    				nxt[i][j] = i;
    			}
    			else nxt[i][j] = nxt[i + 1][j];
    		}
    	}
    	int64 ans = 0;
    	for(int i = 1 ; i <= N ; ++i) {
    		int r = N;
    		for(int j = 0 ; j < 20 ; ++j) {
    			int t = nxt[i][j];
    			if(t != N + 1) r = min(r,nxt[t + 1][j] - 1);
    		}
    		ans += (r - i + 1);
    	}
    	out(ans);enter;
    }
    int main() {
    #ifdef ivorysi
    	freopen("f1.in","r",stdin);
    #endif
    	Solve();
    }
    

    E - Range Minimum Queries

    二分一个差值,枚举最小值,我们会得到一个可取区间
    对于枚举的最小值,我们把区间里所有大于等于它的都标成1,对于一段连续的1区间,统计一下区间里有多少个数在范围内,记为(cnt),设长度为L,这个区间可以满足的询问个数就是(min(L - K + 1,cnt))

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<int,int>
    #define mp make_pair
    #define pb push_back
    #define space putchar(' ')
    #define enter putchar('
    ')
    #define MAXN 2005
    //#define ivorysi
    using namespace std;
    typedef long long int64;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {x = -x;putchar('-');}
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    int N,K,Q;
    int a[MAXN],val[MAXN],tot;
    
    bool check(int mid) {
        for(int i = 1 ; i <= tot ; ++i) {
    	int l = val[i],r = val[i] + mid;
    	int res = 0,st = 0,ed = -1,cnt = 0;
    	for(int j = 1 ; j <= N ; ++j) {
    	    if(a[j] >= l) {
    		if(!st) st = ed = j;
    		ed = max(ed,j);
    		if(a[j] <= r) ++cnt;
    	    }
    	    else {
    		if(ed - st + 1 >= K) {
    		    res += min(cnt,(ed - st + 1) - K + 1);
    		}
    		st = 0,ed = -1;cnt = 0;
    	    }
    	}
    	if(ed - st + 1 >= K) {
    	    res += min(cnt,(ed - st + 1) - K + 1);
    	}
    	if(res >= Q) return true;
        }
        return false;
    }
    void Init() {
        read(N);read(K);read(Q);
        for(int i = 1 ; i <= N ; ++i) {
    	read(a[i]);
    	val[i] = a[i];
        }
        sort(val + 1,val + N + 1);
        tot = unique(val + 1,val + N + 1) - val - 1;
        
    }
    void Solve() {
        int L = 0,R = val[tot] - val[1];
        while(L < R) {
    	int mid = (L + R) >> 1;
    	if(check(mid)) R = mid;
    	else L = mid + 1;
        }
        out(L);enter;
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Init();
        Solve();
    }
    

    F - Donation

    感觉这道题和之前见过的某道题有点一样,又不太一样……
    智商--,看了好久题解才想明白一点。。。

    就是……我们先想一下,我们如果按照每个点交钱的顺序写下一个序列
    对于第k次选走的点,我们需要的是我们钱数(w),需要有(w >= sum_{i = 1}^{k}B_{id[i]} + max(A_{id[k]} - B_{id[k]},0))
    最后我们需要的是序列最大值,我们要最大值最小

    并且,要求这个序列付完钱的点,之后不会再经过了

    我们对于每个点求一个(C[i])(max(A[i] - B[i],0))

    我们发现,如果一个在(C[i])最大值被选择之前,答案的下限在不断提高

    那么我们可能会考虑到,我们要拿走u点之后,我们的图可能会裂成很多子图,我们需要进入其中一个子图,然后将剩下子图的B连带u点累加起来加上(C[u])来累加答案

    那么,在最大值拿走之前,假设最大值拿走之后我们进入的子图是(G_{i}),那么,(G_{i})里的任何一点(w)为什么不能先于u之前拿走呢?

    这是我很难想明白的一件事,我现在也许能谈一下理解

    (w)点在(u)之前时,(B[w])必定会被统计到答案的一个下限中,如果(w)(u)点之后,(B[w])可能不会累加进最后的答案用来更新,也可能会,而我们希望的是答案更优秀,(w)(u)之前答案不可能变小,而(w)(u)之后,答案可能会变小但是绝对不会变大,所以我们把其他子图访问完之后选择访问这个子图

    我们发现,这刚刚好是一个子问题,我们对这个子图取出最大值作为根,然后重新求一下这个子图的答案就可以,我们可以用dp解决这个问题,对于每个子图都进入一遍求值

    然而,好像又有了一个新的问题,我们怎么求每次去掉最大值后,每个子图里的最大值,暴力求显然是(n^2)

    我们发现,全局最大值和每个子图里的最大值连边,每个子图里的最大值和去掉最大值后新裂成的子图最大值连边,刚好可以在图中构建出一棵树

    而C值最小的点,必然是叶子
    我们按C从小到大遍历每个点,访问周边的所有点,如果周边的点有C值小于它的,就让周边的点所在的子图的最大值(用并查集维护)作为它的儿子就好了

    写出来的代码非常清晰,又简单好写,思维含量又高,可以说是虐我这种蒟蒻的好题了。

    代码

    #include <bits/stdc++.h>
    //#define ivorysi
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define eps 1e-8
    #define mo 974711
    #define MAXN 200005
    #define pii pair<int,int>
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
        res = 0;char c = getchar();T f = 1;
        while(c < '0' || c > '9') {
    	if(c == '-') f = -1;
    	c = getchar();
        }
        while(c >= '0' && c <= '9') {
    	res = res * 10 + c - '0';
    	c = getchar();
        }
        res *= f;
    }
    template<class T>
    void out(T x) {
        if(x < 0) {putchar('-');x = -x;}
        if(x >= 10) {
    	out(x / 10);
        }
        putchar('0' + x % 10);
    }
    int N,M;
    struct node {
        int to,next;
    }E[MAXN * 2];
    int head[MAXN],sumE,id[MAXN],ra[MAXN],f[MAXN];
    int64 A[MAXN],B[MAXN],C[MAXN],dp[MAXN],siz[MAXN];
    vector<int> son[MAXN];
    int getfa(int x) {
        return f[x] == x ? x : f[x] = getfa(f[x]);
    }
    bool cmp(int s,int t) {
        return C[s] < C[t];
    }
    void add(int u,int v) {
        E[++sumE].to = v;
        E[sumE].next = head[u];
        head[u] = sumE;
    }
    void Init() {
        read(N);read(M);
        for(int i = 1 ; i <= N ; ++i) {
    	read(A[i]);read(B[i]);C[i] = max(A[i] - B[i],0LL);
    	id[i] = i;
        }
        sort(id + 1,id + N + 1,cmp);
        for(int i = 1 ; i <= N ; ++i) ra[id[i]] = i;
        int u,v;
        for(int i = 1 ; i <= M ; ++i) {
    	read(u);read(v);
    	add(u,v);add(v,u);
        }
    }
    void dfs1(int u) {
        siz[u] += B[u];
        for(auto v : son[u]) {
    	dfs1(v);
    	siz[u] += siz[v];
        }
    }
    void dfs2(int u) {
        if(son[u].size() == 0) {
    	dp[u] = B[u] + C[u];
    	return;
        }
        dp[u] = 1e18;
        for(auto v : son[u]) {
    	dfs2(v);
    	dp[u] = min(siz[u] - siz[v] + max(C[u],dp[v]),dp[u]);
        }
    }
    void Solve() {
        for(int i = 1 ; i <= N ; ++i) f[i] = i;
        for(int i = 1 ; i <= N ; ++i) {
    	int u = id[i];
    	for(int j = head[u] ; j ; j = E[j].next) {
    	    int v = E[j].to;
    	    if(ra[getfa(v)] < ra[u] && getfa(v) != u) {
    		son[u].pb(getfa(v));
    		f[getfa(v)] = u;
    	    }
    	}
        }
        dfs1(id[N]);
        dfs2(id[N]);
        out(dp[id[N]]);enter;
    }
    int main() {
    #ifdef ivorysi
        freopen("f1.in","r",stdin);
    #endif
        Init();
        Solve();
        return 0;
    }
    
  • 相关阅读:
    ruby 类库组成
    ruby 数据类型Number
    ruby require的使用
    ruby $LOAD_PATH及类加载
    ruby编码说明
    RubyMine常用快捷键
    基础
    基础
    基础
    基础
  • 原文地址:https://www.cnblogs.com/ivorysi/p/9849403.html
Copyright © 2011-2022 走看看