2020春季学期ACM周赛(7)
A:货仓选址
cf 800
思路:分析题意即为求这N个数的中位数
代码:*
#include <bits/stdc++.h>
#define CSE(x,y) memset(x,y,sizeof(x))
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
const int maxn = 111111;
int arr[maxn], n;
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
FAST;
cin >> n;
for(int i = 0;i < n;i++){
cin >> arr[i];
}
sort(arr, arr + n);
ll ans = 0;
for(int i = 0;i < n / 2;i++){
ans += arr[n - 1 - i] - arr[i];
}
cout << ans << endl;
return 0;
}
B:数列极差
cf 1200
思路:贪心,举几个例子不难看出,想要得到max值要每次取最小的两个值抹去,想要得到min值要每次选最大的两个值抹去,采用优先队列解决问题。
代码:
#include <bits/stdc++.h>
#define CSE(x,y) memset(x,y,sizeof(x))
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
const int maxn = 111111;
//小顶堆
priority_queue<int, vector<int>, greater<int>> box_mx;
//大顶堆
priority_queue<int, vector<int>, less<int>> box_mn;
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
FAST;
int n;
cin >> n;
for(int i = 0;i < n;i++){
int x;
cin >> x;
box_mn.push(x);
box_mx.push(x);
}
cin >> n;
while(box_mx.size() > 1){
int a1 = box_mx.top();
box_mx.pop();
int a2 = box_mx.top();
box_mx.pop();
box_mx.push(a1 * a2 + 1);
//cout << a1 * a2 << endl;
}
while(box_mn.size() > 1){
int a1 = box_mn.top();
box_mn.pop();
int a2 = box_mn.top();
box_mn.pop();
box_mn.push(a1 * a2 + 1);
//cout << a1 * a2 << endl;
}
cout << abs(box_mn.top() - box_mx.top()) << endl;
return 0;
}
C:数列分段II
cf 1300
思路:二分,二分结果对于每个结果ans,判断是否存在分段方式可以实现,分小于等于M段,且最大值小于等于ans。
代码:
#include <bits/stdc++.h>
#define CSE(x,y) memset(x,y,sizeof(x))
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
const int maxn = 111111;
int arr[maxn], n, m;
bool chack(int k)
{
int sum = 0, area = 1;
for(int i = 0;i < n;i++){
if(sum + arr[i] <= k)
{
sum += arr[i];
}
else
{
sum = arr[i];
area ++;
}
}
return area <= m;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
FAST;
cin >> n >> m;
int l = 0, r = 0;
for(int i = 0;i < n;i++){
cin >> arr[i];
l = max(l, arr[i]);
r += arr[i];
}
while(l <= r){
int mid = l + r >> 1;
if(chack(mid))
{
r = mid - 1;
}
else
{
l = mid + 1;
}
}
cout << r + 1 << endl;
return 0;
}
D:移动玩具
cf 1400
思路:广度优先搜索+状态压缩,根据移动规则,移动玩具,用bfs得到最少移动步数,对于出现过的矩阵状态要做记录,直接记录矩阵比较复杂,但是由于矩阵只有01,所以可以将矩阵看作16位二进制数,对于每个矩阵都可计算出其相应的十进制值,直接标记这个十进制值即可。
代码:
#include <bits/stdc++.h>
#define CSE(x,y) memset(x,y,sizeof(x))
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
const int maxn = 111111;
const int mov[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
bool book[maxn];
struct zt
{
string str;
int hsh;
int step;
};
int myhash(string str)
{
int ans = 0;
for(int i = 0; i < 16; i++)
{
ans = ans * 2 + str[i] - '0';
}
return ans;
}
void swap(char &x, char &y)
{
char temp = x;
x = y;
y = temp;
return;
}
int bfs(string b, string e)
{
queue<zt> box;
int bh = myhash(b);
int eh = myhash(e);
if(bh == eh)
return 0;
box.push(zt{b, bh, 0});
book[bh] = false;
while(!box.empty())
{
zt now = box.front();
box.pop();
for(int i = 0; i < 16; i++)
{
if(now.str[i] == '0')
continue;
for(int j = 0; j < 4; j++)
{
int x = i / 4 + mov[j][0];
int y = i % 4 + mov[j][1];
if(x < 0 || y >= 4|| x >=4 || y <0)
continue;
int ad = x * 4 + y;
if(now.str[ad] == '1')
continue;
string next = now.str;
swap(next[i], next[ad]);
int nh = myhash(next);
if(book[nh])
{
if(nh == eh)
return now.step + 1;
box.push(zt{next, nh, now.step + 1});
book[nh] = false;
}
}
}
}
return -1;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
FAST;
string be, ed;
for(int i = 0; i < 4; i++)
{
string x;
cin >> x;
be += x;
}
for(int i = 0; i < 4; i++)
{
string x;
cin >> x;
ed += x;
}
CSE(book, true);
cout << bfs(be, ed) << endl;
return 0;
}
E:X-factor Chain
cf 1500
思路:前一项能整除后一项说明后一项为前一项的k倍,为了使这个数列尽可能的长,所以每个k都要尽可能取到最小值,k取最小值时应该不能再分解因子,否则存在一个更小的k满足前一项整除后一项,所以k为一个质数,对于每个k都取质因子,问题就变成了,求质因子个数,也就是对原数质因数分解,数列长度为质因子个数,最长数列的个数是质因子的全排列数(注意:相同因子排列在一起先后顺序无影响,所以要排除)
代码:
#include <bits/stdc++.h>
#define CSE(x,y) memset(x,y,sizeof(x))
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
const int maxn = 111111;
int primyz[maxn];
int fenjie(int n,ll &len)
{
int cnt = 0;
len = 0;
for(int i = 2; i * i <= n; i++)
{
if(n % i == 0)
{
primyz[cnt] = 0;
while(n % i == 0)
{
len ++;
n /= i;
primyz[cnt] ++;
}
cnt ++;
}
}
if(n != 1)
{
primyz[cnt] = 1;
cnt++;
len++;
}
return cnt;
}
ll get_pm(ll k)
{
for(int i = k - 1; i > 0; i--)
{
k *= i;
}
return k;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
FAST;
int n;
while(cin >> n)
{
ll len, num, cnt;
cnt = fenjie(n, len);
num = get_pm(len);
for(int i = 0; i < cnt; i++)
{
//cout << num << ':' << primyz[i] << endl;
num /= get_pm(primyz[i]);
}
cout << len << " " << num << endl;
}
return 0;
}
F:Parity game
cf 1700
思路:带权并查集+离散化,对于每次询问给出的区间,如果l-r之间有奇数个1,那么计算前缀和,则sum[l-1] ^ sum[r]=1;偶数个1的情况就是sum[l-1] ^ sum[r]=0;本题没必要求sum值,只需要记录每次询问的结果即可。因为要查找第一个错误的询问,所以对之前的所有询问记录,如果l,r为偶数区间,那么l,r放到一个集合里,之后r,m为奇数区间,那么将m放入l,r集合,这是记录l,m的区间为奇数区间,如果后面出现l,m为偶数区间,那么该询问显然错误。从这个分析过程可以想到,用带权并查集解决问题。
设置一个数组d[ ],表示并查集中x与find(x)之间的关系,也就是sum[x] ^ sum[find(x)] 每次询问,x = l-1,y= r-1,找到find(x) 和 find(y),判断是否在一个集合中,如果再就看d[x]^d[y] 是否等于询问的关系,不满足的话,这个就是错误询问,输出即可,否则将x,y合并入一个集合,操作过程中不断维护d[ ]数组。
因为数列会很长但是询问较少,所以采用离散化
离散化学习:https://www.cnblogs.com/lornd/p/11167316.html
代码:
#include <bits/stdc++.h>
#define CSE(x,y) memset(x,y,sizeof(x))
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
const int maxn = 111111;
int n, m, ls[maxn], d[maxn], pre[maxn], ans, cnt;
struct node
{
int l, r, v;
}p[maxn];
int find(int x)
{
if(pre[x] == x)
return x;
int father = find(pre[x]);
d[x] ^= d[pre[x]];
return pre[x] = father;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
FAST;
cin >> n >> m;
string w;
for(int i = 0; i < m; i++)
{
cin >> p[i].l >> p[i].r >> w;
if(w == "even")
p[i].v = 0;
else
{
p[i].v = 1;
}
ls[++cnt] = p[i].l - 1;
ls[++cnt] = p[i].r;
}
//离散化
sort(ls + 1, ls + 1 + cnt);
int len = unique(ls + 1, ls + 1 + cnt) - ls -1;
for(int i = 1; i <= len; i++)
{
pre[i] = i;
}
for(int i = 0; i < m; i++)
{
int x = lower_bound(ls + 1, ls + 1 + len, p[i].l - 1) - ls;
int y = lower_bound(ls + 1, ls + 1 + len, p[i].r) - ls;
int px = find(x);
int py = find(y);
if(px == py)
{
if(d[x] ^ d[y] != p[i].v)
{
cout << i << endl;
return 0;
}
}
else//merge
{
pre[py] = px;
d[py] = d[x] ^ d[y] ^ p[i].v;
}
}
cout << m << endl;
return 0;
}
G:新NIM游戏
cf 2000
思路:先回想一下传统Nim游戏的性质——如果每堆石子数量的异或和为0,那么先手必败;否则先手必胜。那么现在在新的游戏规则下,如果轮到后手时,存在一个子集使得异或和为0,那么后手就是必胜的。所以先手需要用最小的代价把局面变成不存在任何一个子集异或和为0,这不就是线性基嘛。所以贪心的选取尽可能大的堆组成线性基即可。
线性基学习:https://www.cnblogs.com/yangsongyi/p/10692292.html
代码:
#include <bits/stdc++.h>
#define CSE(x,y) memset(x,y,sizeof(x))
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
const int maxn = 111;
int n, a[maxn], b[maxn], c, f[maxn];
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
FAST;
cin >> n;
ll ans = 0ll;
for(int i = 1; i <= n; i++)
{
cin >> a[i];
}
sort(a + 1, a + 1 + n);
for(int i = 1; i <= n; i++)
{
ans += b[i] = a[i];
}
for(int i = n; i >= 1; i--)
{
for(int j = 31; ~j; j--)
{
if((a[i] >> j) & 1)
{
if(f[j])
a[i] ^= f[j];
else
{
f[j] = a[i];
break;
}
}
}
if(a[i])
ans -= b[i];
}
cout << ans << endl;
return 0;
}