洛谷8月月赛(Div.2)题解(A - C)

洛谷8月月赛(Div.2)题解(A - C)

转载于:此篇文章
事先声明:两篇文章(其实都是一篇文章)都是我一个人写的,
只是两个ID号。

A - 「RiOI-2」hacker

传送门

根据“或”与“和”的性质

我们可以得出:“或”单调不减,“与”单调不增

把对于二进制下的m和n,我们只要或上m有n没有的二进制位所代表的数,与上n有m没有的二进制位所代表的数,就可以求出来了

两个计数器:cnt1 = 0,cnt2 = 0

如果某一位m有n没有,cnt1 = 1,如果一位n有m没有,cnt2 = 1

最后输出:cnt1 + cnt2

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
#define all(s) s.begin(),s.end()
const int M = 1005;
typedef long long ll;
#define endl "\n"
queue <ll> q;
map <ll,ll> cnt;
ll lowbit(ll x){
    return x&(-x);
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int T;
    cin >> T;
    while (T --){
        ll n , m;
        cin >> n >> m;
        ll cnt1 = 0,cnt2 = 0;
        for (ll i = 0 ; i <= 64 ;i ++){
            bool t1 = false, t2 = false;
            if ((1ll << i) & n) t1 = true;
            if ((1ll << i) & m) t2 = true;
            if (t1 && (!t2)) cnt1 = max(cnt1 , 1ll);
            if ((!t1) && t2) cnt2 = max(cnt2 , 1ll);
        }
        cout << cnt1 + cnt2 << endl;
    }
    return 0;
}

B - 「RiOI-2」weight

传送门

前言:一定不能读错题,不然难度暴增10倍

为什么卡常还不开O(2)啊啊啊!!!

卡常

因为每一行可以任意排,所以对于每一个v,我们只要求出 >= 它的数的个数就行了

因为,我们可以把大于它的数,分散到每一列。

所以,我们只要记录每个数出现的个数,并求出前缀和,对于sum[i]表示 <= i的数的个数

所以代码为:

#include <bits/stdc++.h>
using namespace std;
const int N = 5e6 + 5;
#define all(s) s.begin(),s.end()
const int M = 1005;
typedef long long ll;
#define endl "\n"
map <int,ll> mp;
int a[N];
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int n , m;
    cin >> n >> m;
    int k = 0;
    ll sum = 0;
    for (int i = 1 ; i <= n ; i ++) {
        for (int j = 1 ; j <= n ; j ++) {
            int x;
            cin >> x;
            if (!mp[x]) a[++ k] = x;
            mp[x] ++;
            sum ++;
        }
    }
    sort (a + 1 , a + k + 1);
    for (int i = 1 ; i <= k ; i ++) mp[a[i]] += mp[a[i - 1]];
    while (m --) {
        int v;
        cin >> v;
        int p = lower_bound(a + 1 , a + k + 1 , v) - a;
        p --;
        // cout << "1 " << p << endl;
        cout << min(n * 1ll , sum - mp[a[p]]) << endl;
    }
    return 0;
}
交一发:哇!T!

为什么!为什么,上天为什么要这样对待我?!(这是一个疯子,不要理会他)

理论上来说,复杂度应该是 O ( Q × log ⁡ 2 n ) O(Q\times \log_2n) O(Q×log2n),但由于我们map的次数调用过多,被洛谷卡常了(为什么要用map呢?因为sum的下标是值域即 1 0 9 10^9 109 , 数组存不下),

那怎么办?

于是:我们引入一个神奇的优化方法:离散化

离散化是什么?

就是在不考虑值,只考虑的情况下,我们可以用离散化将一个空间复杂度 O ( 值域 ) O(值域) O(值域) 的数组优化成 O ( 近似于 n ) O(近似于n) O(近似于n)的神奇方法。

然后,代码优化为:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
#define all(s) s.begin(),s.end()
const int M = 1005;
typedef long long ll;
#define endl "\n"
map <int,int> mp;
inline void read(int &x){
   int s = 0; char ch = getchar();
   while(ch < '0' || ch > '9'){ch = getchar(); }
   while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
   x = s;
}
struct Node {
    int x , val;
    bool operator < (const Node & A) const{
        return x < A.x;
    }
}a[N];
int cnt[N],b[N];
signed main(){
    int n , m;
    read(n) , read(m);
    int k = 0,sum = 0,i,j,x;
    for (i = 1 ; i <= n ; i ++) {
        for (j = 1 ; j <= n ; j ++) {
            scanf("%d",&x);
            int &l = mp[x];
            if (!l){
                a[++ k].x = x;
                a[k].val = 0;
                l = k;
            }
            a[l].val ++;
        }
    }
    sum = n * n;
    sort (a + 1 , a + k + 1);
    for (i = 1 ; i <= k ; i ++) b[i] = a[i].x;
    for (i = 1 ; i <= k ; i ++) cnt[i] = cnt[i - 1] + a[i].val;
    // for (i = 1 ; i <= k ; i ++) cout << cnt[i] << " ";
    // cout << endl;
    int v,p;
    while (m --) {
        read(v);
        p = lower_bound(b + 1 , b + k + 1 , v) - b;
        p --;
        // cout << "1 " << p << endl;
        printf("%d\n", min(n , sum - cnt[p]));
    }
    return 0;
}

评测机:

为什么还是T了啊。

敲黑板!说重点!对于一些神奇的评测机(非OI赛制评测机),评测非常不稳定,所以,一波动,你本来可以卡过去的代码,T了100ms-,所以,解决方案为:再交一发!

AC!

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
#define all(s) s.begin(),s.end()
const int M = 1005;
typedef long long ll;
map <int,int> mp;
inline void read(int &x){
   int s = 0; char ch = getchar();
   while(ch < '0' || ch > '9'){ch = getchar(); }
   while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
   x = s;
}
struct Node {
    int x , val;
    bool operator < (const Node & A) const{
        return x < A.x;
    }
}a[N];
int cnt[N],b[N];
signed main(){
    int n , m;
    read(n) , read(m);
    int k = 0,sum = 0,i,j,x;
    for (i = 1 ; i <= n ; i ++) {
        for (j = 1 ; j <= n ; j ++) {
            scanf("%d",&x);
            int &l = mp[x];
            if (!l){
                a[++ k].x = x;
                a[l = k].val = 0;
            }
            a[l].val ++;
        }
    }
    sum = n * n;
    sort (a + 1 , a + k + 1);
    for (i = 1 ; i <= k ; i ++) b[i] = a[i].x;
    for (i = 1 ; i <= k ; i ++) cnt[i] = cnt[i - 1] + a[i].val;
    // for (i = 1 ; i <= k ; i ++) cout << cnt[i] << " ";
    // cout << endl;
    int v,p;
    while (m --) {
        read(v);
        p = lower_bound(b + 1 , b + k + 1 , v) - b;
        p --;
        // cout << "1 " << p << endl;
        printf("%d\n", min(n , sum - cnt[p]));
    }
    return 0;
}

C - 「RiOI-2」equals

传送门

前言:为了纪念我在这道题上的优秀表现,我先亮图:


真的:

题解

首先,我们得根据题意求出每个点的深度。

然后我们把它排序。

现在我们把题目重新解读一遍:你是一个幼儿园的老师,你要把n块蛋糕分给2个小朋友,每块重 a i a_i aig(每个 a i a_i ai ≤ a i + 1 \le a_{i+1} ai+1),如果其中一个比另一个少的话,那少的那个小朋友就会很生气,为了保住工资,你必须让小朋友们都不生气。

显然,对于 ∑ i = 1 n a [ i ] \sum_{i = 1}^{n} a[i] i=1na[i] % 2 == 1,无解,你就等着扣工资吧(输出-1)。

每两个数算一组。

首先,对于 a i = a i + 1 a_i = a_{i+1} ai=ai+1,我们只要把一人给一块就好了。

如果 a i = a i + 1 − 1 a_i=a_{i+1}-1 ai=ai+11,我们给其中一个(小B) a i a_i ai,另一个(小C) a i + 1 a_{i+1} ai+1,并在下一次 a i ≠ a i + 1 a_i \ne a_{i+1} ai=ai+1时,再给小B a i + 1 a_{i+1} ai+1,小C a i a_{i} ai,相当于,你在以欠了小B 1克,你把它补回来。

对于 n n n是奇数的情况,我们要先补一个0,把n变成偶数,这样就好做了。

注意一些细节:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
#define all(s) s.begin(),s.end()
const int M = 1005;
#define int long long
int cur;
typedef long long ll;
#define endl "\n"
vector <ll> G[N];
pair <ll,ll> dep[N];
ll k = 0;
ll sum = 0;
void DFS(int u, int fa) {
	dep[u] = make_pair(dep[fa].first + 1, u);
	for (int i = 0; i < G[u].size(); i++) {
		int v = G[u][i];
		if (v != fa) DFS(v, u);
	}
}
int col[N];
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1 ; i <= n - 1 ;i ++){
        ll u , v;
        cin >> u >> v;
        G[u].push_back(v),G[v].push_back(u);
    }
    DFS(1 , 0);
    bool t = false;
    if (n%2) n ++, t = true;
    sort(dep + 1 , dep + n + 1);
    for (int i = 1 ; i <= n  ;i ++) sum += dep[i].first;
    if (sum%2) {cout << -1 << endl;return 0;};
    for (int i = 1 ; i <= n; i += 2) {
        if (dep[i].first == dep[i + 1].first) col[dep[i].second] = 0 , col[dep[i + 1].second] = 1;
        else{
            if (cur == -1){
                col[dep[i].second] = 1;
                col[dep[i + 1].second] = 0;
                cur = 0;
            }
            else {
                col[dep[i].second] = 0;
                col[dep[i + 1].second] = 1;
                cur = -1;
            }
        }
    }
    for (int i = 1 ; i <= n - t; i ++) cout << col[i] << " ";
    return 0;
}

T H E   E N D . \huge THE\ END. THE END.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值