Deltix Round, Spring 2021 题解 A-E
A Game of Life
题意
给定长度(n)的01串,其中的0每次有可能变成1,条件是这个0的左右有且仅有1个1
问(m)次操作后的01串
分析
可以把这个操作变成1向左右拓展,两个1之间长度为奇数的话要么拓展到一半结束了,要么中间空一个,偶数可以填满
注意左右边界的情况
根据上述转化模拟即可
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll rd(){
ll x;
scanf("%lld",&x);
return x;
}
const int maxn = 1e6 + 5;
void solve(){
int n = rd();
int m = rd();
string s;
cin >> s;
vector<int> v;
for(int i = 0;i < s.length();i++){
if(s[i] == '1') v.push_back(i);
}
string ans;
if(v.empty()) cout << s << '
';
else {
int head = v[0];
int mi = min(head,m);
for(int i = 0;i < head - mi;i++) ans.push_back('0');
for(int i = 0;i < mi;i++) ans.push_back('1');
for(int i = 0;i < v.size() - 1;i++){
ans.push_back('1');
int tmp = v[i + 1] - v[i] - 1;
int dis = tmp;
tmp /= 2;
if(1) {
mi = min(tmp,m);
for(int i = 0;i < mi;i++) ans.push_back('1');
for(int i = 0;i < dis - 2 * mi;i++) ans.push_back('0');
for(int i = 0;i < mi;i++) ans.push_back('1');
}
}
ans.push_back('1');
mi = min(m,n - 1 - v.back());
for(int i = 0;i < mi;i++) ans.push_back('1');
int len = ans.length();
for(int i = 0;i < n - len;i++) ans.push_back('0');
cout << ans << '
';
}
}
int main(){
int T = rd();
while(T--)
solve();
}
B Lord of the Values
给定长度(n)的数组(a)
(n)为偶数
通过如下两个操作 要把每个元素变为当前数的相反数
- (a_i =a_i + a_j)
- (a_j = a_j - a_i)
注意条件(i < j)
分析
想想为什么给偶数,再结合这只是B题,往简单的想能够想到仅对两个数操作
只需要执行121212即可,操作次数(3 * n)
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll rd(){
ll x;
scanf("%lld",&x);
return x;
}
const int maxn = 1e6 + 5;
void solve(){
int n = rd();
vector<int> v(n + 1);
cout << n * 3 << '
';
for(int i = 1;i <= n;i++)
v[i] = rd();
for(int i = 1;i <= n;i += 2) {
cout << "1 " << i << ' ' << i + 1 << '
';
cout << "2 " << i << ' ' << i + 1 << '
';
cout << "1 " << i << ' ' << i + 1 << '
';
cout << "2 " << i << ' ' << i + 1 << '
';
cout << "1 " << i << ' ' << i + 1 << '
';
cout << "2 " << i << ' ' << i + 1 << '
';
}
}
int main(){
int T = rd();
while(T--) solve();
}
C Compression and Expansion
题意
制作一个目录一样的东西
目录规则:
要么在当前的后面加上".1"表示新开启一节
要么回到上一个并且让节数+1
如
1
1.1
1.1.1
1.1.2
1.2
1.2.1
2
2.1
2.2
形如这样的排版是合法的
现给出一组目录的最后一个数字,要求构造出一种排版方案,保证存在一种方案
分析
容易想到一种贪心策略,如果有新的1,那么直接无脑新开一节,加在末尾即可,如果不是1,为了保证合法,先前一定有一个数保证该数等于加入的数-1,这样只需要回退即可
于是这题就是变成了类似栈的模拟题
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll rd(){
ll x;
scanf("%lld",&x);
return x;
}
const int maxn = 1e6 + 5;
void solve(){
int n = rd();
vector<int> v;
vector<string> ans;
int x = rd();
ans.push_back("1");
v.push_back(1);
cout << ans[0] << '
';
for(int i = 2;i <= n;i++){
int x = rd();
if(x == 1) ans.push_back(".1"),v.push_back(1);
else {
while(!v.empty() && v.back() != x - 1) v.pop_back(),ans.pop_back();
v.pop_back(),ans.pop_back();
v.push_back(x);
string tmp;
while(x){
tmp.push_back(x % 10 + '0');
x /= 10;
}
if(!ans.empty()) tmp.push_back('.');
reverse(tmp.begin(),tmp.end());
ans.push_back(tmp);
}
for(int i = 0;i < ans.size();i++)
cout << ans[i];
puts("");
}
}
int main(){
int T = rd();
while(T--) solve();
}
D Love-Hate
题意
集合中给出(n)个元素,每个元素表示长度为(m)的01串,保证每个01串1的个数不会超过15个
求出一个01串,这个01串至少是(lceil frac{n}{2} ceil) 个元素的子集,且1的个数最多
分析
这个01串一定是(n)个元素中的某个元素的子集
考虑随机选其中的若干个元素,由于至少要一半,那么随机选取的元素中一个答案元素都没有的可能性很小
因此枚举随机到的元素
考虑这个元素的子集,将这个元素和其他所有元素做与运算,会得到若干个状态,记录到cnt数组中,cnt数组表示这个元素能够作为答案的出现个数,因此还要做一次高维前缀和
然后就只要2进制枚举一下这个出现次数是否满足要求,是否能够更新答案即可
复杂度(O(it * p * (2 ^p + n)))
注意控制随机次数
代码
#include<bits/stdc++.h>
#define re register
using namespace std;
typedef long long ll;
int rd(){
int x = 0;
char ch = getchar();
while(ch < '0' || ch > '9') {
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x;
}
const int maxn = 2e5 + 5;
const int MOD = 1e9 + 7;
char s[80];
ll a[maxn];
int b[(1 << 15) + 5];
int main(){
int n = rd();
int m = rd();
int k = rd();
ll ans = 0;
int cnt_ans = 0;
for(re int i = 0;i < n;i++){
scanf("%s",s);
a[i] = 0;
for(int j = 0;j < m;j++)
if(s[j] == '1') a[i] |= (1ll << j);
}
random_shuffle(a,a + n);
for(re int it = 0;it < n && it < 130;it++){
vector<int> pos;
for(re int i = 0;i < m;i++)
if((a[it] >> i) & 1) pos.push_back(i);
k = (int)pos.size();
for(re int i = 0;i < (1 << k);i++)
b[i] = 0;
for(re int i = 0;i < n;i++){
int msk = 0;
for(re int j = 0;j < k;j++)
if((a[i] >> pos[j]) & 1)
msk |= (1 << j);
b[msk]++;
}
for(re int i = 0;i < k;i++){
for(re int msk = 0;msk < (1 << k);msk++){
if((msk >> i) & 1) continue;
b[msk] += b[msk | (1 << i)];
}
}
for(re int msk = 0;msk < (1 << k);msk++){
if(2 * b[msk] < n) continue;
int cnt = 0;
for(re int i = 0;i < k;i++)
cnt += (msk >> i) & 1;
if(cnt > cnt_ans) {
cnt_ans = cnt;
ans = 0;
for(re int i = 0;i < k;i++)
if((msk >> i) & 1) ans ^= (1ll << pos[i]);
}
}
}
for(re int i = 0;i < m;i++)
printf("%lld",(ans >> i) & 1);
}
E Crypto Lights
题意
给定(n,k)
长度为(n)的01串,每次会随机一个非(1)位置将(0)变为(1),当任意两个(1)之前的距离小于(k - 1)时结束,求结束时的1的个数的期望
分析
直接套用期望的公式
可以把结束后的期望转化为结束前的期望
要求(geq i)的概率,考虑当前有多少种情况,即有
种
对于本身的放置情况,(i)个1把1分成$i -1 (组,任意组之间的距离至少是)k - 1$
此时定好的1的个数和中间0的个数加起来至少有(n - i - (k - 1)(i - 1)),剩下的随意放置。
只需要做隔板法即可
再考虑上顺序问题 即可得出现的情况总共有( binom{n - (k - 1)(i - 1)}{i} imes i!)
因此
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll rd(){
ll x;
scanf("%lld",&x);
return x;
}
const int maxn = 1e5 + 5;
const int MOD = 1e9 + 7;
ll ksm(ll a,ll b = MOD - 2,ll m = MOD){
ll ans = 1;
ll base = a;
while(b){
if(b & 1) ans *= base,ans %= m;
base *= base;
base %= m;
b >>= 1;
}
return ans;
}
ll fac[maxn],iv[maxn];
void solve(){
int n = rd();
int k = rd();
k--;
ll ans = 1;
for(int i = 0;n - i * k >= i + 1;i++)
ans += ((ll)fac[n - i * k]) * iv[n - i * k - i - 1] % MOD * iv[n] % MOD * fac[n - i - 1] % MOD,ans %= MOD;
cout << ans << '
';
}
int main(){
fac[0] = 1;
for(int i = 1;i < maxn;i++) fac[i] = (ll)fac[i - 1] * i % MOD;
iv[maxn - 1] = ksm(fac[maxn - 1]);
for(int i = maxn - 1;i;i--)
iv[i - 1] = (ll)iv[i] * (i) % MOD;
int T = rd();
while(T--) solve();
}