• 2018CCPC吉林赛区


    传送门

    A - The Fool

    整除分块即可。

    B - The World

    模拟即可。

    C - Justice

    题意:
    给出(n)个数(k_i),每个数的权值为(frac{1}{2^{k_i}})
    现在问能否将这些数划分为两个集合,使得每个集合里面数的权值和不小于(frac{1}{2})
    若合法,输出任意一种方案。

    思路:

    • 对于两个相同的(k),一定能够合并为(k-1)
    • 直接数组存储显然存不下,我们可以直接合并到(k-x),满足(k-x)存在,也就是说我们每次将(a_i)减小到(a_{i-1}),时间复杂度最多为(O(logn))
    • 模拟这个过程就行了,后面输出方案的时候找到一个阀值即可。

    说起来感觉比较简单,写起来没那么简单,细节很多。。后面寻找方案的时候也套了个二分(可能我姿势水平太低了)。

    Code
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    // #define Local
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int N = 1e5 + 5;
    
    int n;
    pii a[N], b[N], c[N];
    int Case;
    int bel[N], ans[N];
    
    bool ok(int x, int y) {
    	while(x != 1 && y >= 2) {
    		--x; y /= 2;
    	}
    	if(x == 1) return true;
    	return false;
    }
    
    void run() {
    	++Case;
    	cin >> n;
    	for(int i = 1; i <= n; i++) {
    		cin >> a[i].fi;
    		a[i].se = i;
    		bel[i] = ans[i] = 0;
    	}
    	sort(a + 1, a + n + 1);
    	int tot = 0;
    	for(int i = 1, j; i <= n; i = j) {
    		j = i;
    		while(j <= n && a[i].fi == a[j].fi) ++j;
    		b[++tot] = MP(a[i].fi, j - i);
    		for(int k = i; k < j; k++) bel[k] = tot;
    	}
    	b[0] = MP(1, 0);
    	for(int i = 0; i <= tot; i++) c[i] = b[i];
    	for(int i = tot; i > 0; i--) {
    		int x = b[i].se;
    		int now = b[i].fi, pre = b[i - 1].fi;
    		while(now != pre && x >= 2) {
    			x /= 2;
    			--now;
    		}		
    		if(now == pre) b[i - 1].se += x;
    	}
    	cout << "Case " << Case << ": ";
    	if(b[0].se >= 2) {
    		cout << "YES" << '
    ';
    	} else {
    		cout << "NO" << '
    ';
    		return;
    	}
    	if(a[1].fi == 1) {
    		ans[a[1].se] = 1;
    		for(int i = 1; i <= n; i++) {
    			cout << ans[i];
    		}
    		cout << '
    ';
    		return;
    	}
    	int need = 0, t = 0;
    	for(int i = tot; i > 0; i--) {
    		int x = c[i].se;
    		int now = c[i].fi, pre = c[i - 1].fi;
    		if(ok(now, x)) {
    			int l = 1, r = x + 1, mid;
    			while(l < r) {
    				mid = (l + r) >> 1;
    				if(ok(now, mid)) r = mid;
    				else l = mid + 1;
    			}
    			t = tot + 1, need = r; break;
    		}
    		while(now != pre && x >= 2) {
    			x /= 2;
    			--now;
    		}
    		if(now != pre) x = 0;
    		int k1 = c[i - 1].fi, k2 = c[i - 1].se + x;
    		if(ok(k1, k2)) {
    			int l = x, r = k2 + 1, mid;
    			while(l < r) {
    				mid = (l + r) >> 1;
    				if(ok(k1, mid)) r = mid;
    				else l = mid + 1;
    			}
      			need = r - x; t = i; break;
    		}
    		c[i - 1].se += x;
    	}
    	for(int i = 1; i <= n; i++) {
    		if(bel[i] >= t) {
    			ans[a[i].se] = 1; 
    		} else if(bel[i] == t - 1 && need) {
    			ans[a[i].se] = 1; --need;
    		} else ans[a[i].se] = 0;
    	}
    	for(int i = 1; i <= n; i++) {
    		cout << ans[i];
    	}
    	cout << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
    #ifdef Local
        freopen("../input.in", "r", stdin);
        freopen("../output.out", "w", stdout);
    #endif
        int T; cin >> T;
        while(T--) run();
        return 0;
    }
    

     
    有一种更加简单优美的做法:并查集+优先队列。
    并查集就维护合并路径,还是比较巧妙。
    细节见代码:

    Code
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    // #define Local
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int N = 1e5 + 5;
    
    int n;
    int a[N];
    int Case;
    
    int f[N];
    int find(int x) {
    	return f[x] == x ? f[x] : f[x] = find(f[x]);
    }
    void run() {
    	cout << "Case " << ++Case << ": ";
    	cin >> n;
    	for(int i = 1; i <= n; i++) cin >> a[i];
    	for(int i = 0; i <= n; i++) {
    		f[i] = i;
    	}
    	priority_queue <pii> q;
    	for(int i = 1; i <= n; i++) {
    		q.push(pii(a[i], i));
    	}
    	while(!q.empty() && q.top().fi > 1) {
    		pii now = q.top(); q.pop();
    		if(q.empty()) break;
    		if(now.fi != q.top().fi) continue;
    		pii cur = q.top(); q.pop();
    		int fx = find(now.se), fy = find(cur.se);
    		if(fx < fy) swap(fx, fy);
    		f[fx] = fy;
    		q.push(pii(now.fi - 1, fy));
    	}
    	if(q.size() <= 1) {
    		cout << "NO" << '
    ';
    	} else {
    		cout << "YES" << '
    ';
    		for(int i = 1; i <= n; i++) {
    			if(find(i) == q.top().se) cout << 1;
    			else cout << 0;
    		}
    		cout << '
    ';
    	}
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
    #ifdef Local
        freopen("../input.in", "r", stdin);
        freopen("../output.out", "w", stdout);
    #endif
        int T; cin >> T;
        while(T--) run();
        return 0;
    }
    

    D - The Moon

    数学期望,就考虑几种情况就行,倒着来推,递推式详见代码:(因为有(1.5\%),所以是千分位)

    Code
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int MAXN = 1e3+5;
    typedef double db;
    int t,p;
    db f[MAXN];
    db g(int x){
        return f[min(x,1000)];
    }
    int main() {
        scanf("%d",&t);
        for(int kase=1;kase<=t;kase++){
            scanf("%d",&p);
            f[1000] = 100.0/p;
            for(int i=995;i>=20;i-=5)
                f[i] = (p/100.0)*(i/1000.0) + (p/100.0)*((1000-i)/1000.0)*(1+g(i+20)) + (100-p)/100.0*(1+g(i+15));
            printf("Case %d: %.7f
    ",kase,f[20]);
        }
        return 0;
    }
    

    F - The Hermit

    仔细分析一下题目条件就很好做了,每一个位置答案为(a_i-2)

    H - Lovers

    题意:
    一开始有(n)个空串(s_1,s_2,cdots,s_n),然后执行(m)次操作,操作分为两种:

    • (w l r d:)(l)(r)位置上的所有子串两边加上(d),相当于(s_i)变为(ds_id,lleq ileq r)
    • (q l r:)询问(sum_{i=l}^r value*(s_i)(mod 10^9+7))

    思路:
    硬核线段树。
    考虑若对一个位置施加一次操作,结果为:

    [10cdot dcdot sum_2+10cdot sum+d ]

    其中(sum)表示对应区间的答案和,(sum_2)表示对应区间的(sum 10^{len_i})
    三个的含义分别是头、中、尾部。
    手玩一下,发现如果施加三次操作,结果将变为:

    [10^3cdot sum_2cdot [d_3d_2d_1]+10^3cdot sum+[d_1d_2d_3] ]

    那么我们可以发现,我们维护懒标记时,可以单独维护([d_1cdots d_n],[d_ncdots d_1],10^{len}),这样懒标记的更新也很好更新,懒标记下传时直接对应乘起来即可。
    其中([d_1d_2cdots d_n])表示(value(d_1d_2cdots d_n))

    一开始细节没有想清楚,感觉还是有点遗憾。
    注意一下(sum_2)的更新。
    详见代码:

    Code
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    // #define Local
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int N = 1e5 + 5, MOD = 1e9 + 7;
    
    int Case;
    int n, m;
    int sum[N << 2], sum2[N << 2];
    int llz[N << 2], rlz[N << 2], lenlz[N << 2];
    char s[10];
    
    void push_up(int o) {
    	sum[o] = (sum[o << 1] + sum[o << 1|1]) % MOD;
    	sum2[o] = (sum2[o << 1] + sum2[o << 1|1]) % MOD;
    }
    
    void upd(int son, int fa, int l, int r) {
    	sum[son] = (1ll * lenlz[fa] * sum2[son] % MOD * llz[fa] % MOD + 
    				1ll * lenlz[fa] * sum[son] % MOD + 
    				1ll * rlz[fa] * (r - l + 1) % MOD) % MOD;
    	sum2[son] = 1ll * lenlz[fa] * lenlz[fa] % MOD * sum2[son] % MOD;
    	llz[son] = (1ll * llz[fa] * lenlz[son] % MOD + llz[son]) % MOD;
    	rlz[son] = (1ll * rlz[son] * lenlz[fa] % MOD + rlz[fa]) % MOD;
    	lenlz[son] = 1ll * lenlz[son] * lenlz[fa] % MOD;
    }
    
    void push_down(int o, int l, int r) {
    	if(lenlz[o] > 1) {
    		int mid = (l + r) >> 1;
    		upd(o << 1, o, l, mid);
    		upd(o << 1|1, o, mid + 1, r);
    		lenlz[o] = 1;
    		llz[o] = rlz[o] = 0;
    	}
    }
    
    void build(int o, int l, int r) {
    	llz[o] = rlz[o] = 0;
    	lenlz[o] = 1;
    	if(l == r) {
    		sum[o] = 0; sum2[o] = 1;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	build(o << 1, l, mid);
    	build(o << 1|1, mid + 1, r);
    	push_up(o);
    }
    
    void update(int o, int l, int r, int L, int R, int d) {
    	if(L <= l && r <= R) {
    		sum[o] = (1ll * sum[o] * 10 % MOD + 1ll * d * (r - l + 1) % MOD + 1ll * 10 * d * sum2[o] % MOD) % MOD;
    		sum2[o] = 1ll * sum2[o] * 100 % MOD;
    		
    		llz[o] = (1ll * lenlz[o] * d % MOD + llz[o]) % MOD;
    		rlz[o] = (1ll * rlz[o] * 10 + d) % MOD;
    		lenlz[o] = 1ll * lenlz[o] * 10 % MOD;
    		return;
    	}
    	push_down(o, l, r);
    	int mid = (l + r) >> 1;
    	if(L <= mid) update(o << 1, l, mid, L, R, d);
    	if(R > mid) update(o << 1|1, mid + 1, r, L, R, d);
    	push_up(o);
    }
    
    int query(int o, int l, int r, int L, int R) {
    	if(L <= l && r <= R) {
    		return sum[o];
    	}
    	push_down(o, l, r);
    	int mid = (l + r) >> 1, res = 0;
    	if(L <= mid) res = (res + query(o << 1, l, mid, L, R)) % MOD;
    	if(R > mid) res = (res + query(o << 1|1, mid + 1, r, L, R)) % MOD;
    	return res;
    }
    
    void run() {
    	++Case;
    	cout << "Case " << Case << ":" << '
    ';
    	cin >> n >> m;
    	build(1, 1, n);
    	while(m--) {
    		cin >> s;
    		int l, r, d;
    		if(s[0] == 'w') {
    			cin >> l >> r >> d;
    			update(1, 1, n, l, r, d);
    		} else {
    			cin >> l >> r;
    			int ans = query(1, 1, n, l, r);
    			cout << ans << '
    ';
    		}
    	}
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
    #ifdef Local
        freopen("../input.in", "r", stdin);
        freopen("../output.out", "w", stdout);
    #endif
        int T; cin >> T;
        while(T--) run();
        return 0;
    }
    

    I - Strength

    游戏王,憨憨贪心,分两种情况就行。

    Code
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    // #define Local
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int MAXN = 1e5+5;
    int t,n,m,a[MAXN],b[MAXN],c[MAXN];
    multiset<int> st;
    bool vis[MAXN];
    int main(){
        cin>>t;
        for(int kase=1;kase<=t;kase++){
            cin>>n>>m;
            for(int i=1;i<=n;i++)vis[i]=0;
            st.clear();
            for(int i=1;i<=n;i++)cin>>a[i];
            for(int i=1;i<=m;i++)cin>>b[i];
            int k=0;
            for(int i=1;i<=m;i++){
                int x;
                cin>>x;
                if(x==1)st.insert(b[i]);
                else c[++k] = b[i];
            }
            sort(a+1,a+1+n);
            sort(c+1,c+1+k);
            ll ans=0;
            for(int i=1;i<=n;i++){
                auto it = st.lower_bound(a[i]);
                if(*it == a[i]){
                    st.erase(it);
                    vis[i]=1;
                }else if(it!=st.begin()){
                    it--;
                    st.erase(it);
                    vis[i]=1;
                }
            }
            ll sum = 0;
            if(st.size()==0){
                int j=1;
                for(int i=n;i>=1;i--){
                    if(vis[i])continue;
                    if(j<=k)sum += max(0,a[i] - c[j++]);
                    else sum += a[i];
                }
            }
            ans = sum;
            int j=1;
            sum=0;
            for(int i=n;i>=1&&j<=k;i--){
                sum += max(0,a[i]-c[j++]);
            }
            ans=max(ans,sum);
            cout<<"Case "<<kase<<": "<<ans<<'
    ';
        }
        return 0;
    }
    
  • 相关阅读:
    Django 框架 # 51
    Django 框架 介绍# 51
    前端之Bootstrap框架 # 50
    phpcms调用一个指定的栏目的url和栏目名称
    phpcms导航栏调用二级栏目
    彻底弄懂JS的事件冒泡和事件捕获
    toggle 方法的使用
    关于内层DIV设置margin-top不起作用的解决方案
    phpmyadmin导入数据库大小限制修改
    phpcms v9 的表单向导功能的使用方法
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11706054.html
Copyright © 2020-2023  润新知