文章目录
洛谷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+1−1,我们给其中一个(小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.