本场链接:Deltix Round, Spring 2021 (open for everyone, rated, Div. 1 + Div. 2)
闲话
E估计是补不了,看情况吧。
A. Game of Life
直接模拟,至多迭代\(n-1\)次,如果没有修改的操作就直接退出。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
const int N = 1e3+7;
char s[N];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n,m;scanf("%d%d",&n,&m);
scanf("%s",s + 1);s[n + 2] = '0';s[0] = '0';
while(m--)
{
vector<int> ch;
forn(i,1,n)
{
if(s[i] == '1') continue;
int cnt = 0;
if(s[i - 1] == '1') ++cnt;
if(s[i + 1] == '1') ++cnt;
if(cnt == 1) ch.push_back(i);
}
if(!ch.size()) break;
for(auto& v : ch) s[v] = '1';
}
printf("%s\n",s + 1);
}
return 0;
}
B. Lord of the Values
为什么\(n\)是个偶数?每两个数之间肯定存在一种操作序列能得到答案:交替使用\(2 1 2 1 2 1\)即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
const int N = 1e3+7;
struct Node
{
int a,b,c;
};
int a[N];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n;scanf("%d",&n);
forn(i,1,n) scanf("%d",&a[i]);
vector<Node> op;
for(int i = 2;i <= n;i += 2) forn(_,1,3) op.push_back({2,i - 1,i}),op.push_back({1,i - 1,i});
printf("%d\n",(int)op.size());
for(auto& _ : op) printf("%d %d %d\n",_.a,_.b,_.c);
}
return 0;
}
C. Compression and Expansion
其实是个模拟题:
如果来的是个\(1\),那么直接加入序列。
如果来的数\(>1\),不断弹出序列末尾的不是当前来的数减一的数直到为空。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n;scanf("%d",&n);
vector<int> from;
forn(i,1,n)
{
int a;scanf("%d",&a);
if(a == 1) from.push_back(1);
else
{
while(!from.empty() && from.back() != a - 1) from.pop_back();
if(from.empty()) from.push_back(a);
else from.pop_back(),from.push_back(a);
}
for(int j = 0;j < from.size();++j)
{
printf("%d",from[j]);
if(j != from.size() - 1) printf(".");
}
puts("");
}
}
return 0;
}
D. Love-Hate
答案一定是某个方案的子集,那么枚举每个方案作为模板去和其他每个方案求交集似乎是个可行的思路,考虑枚举之后的部分:
由于\(m\)比较大而\(p\)比较小,因此可以状压的只有\(p\),将状态设计成是否选择模板中的每个\(1\)。以此与每个方案求交集(在模板的范围内),现在的问题就是需要找出一个可行的方案,他被覆盖到的次数超过\((n+1)/2\),这个可以求一个和:状态\(S\)和所有\(S\)的父集被覆盖的次数就是原有方案可以覆盖到的个数。以此更新答案即可。
现在的问题就是,子集求和基本的复杂度是\(O(3^n)\),即使使用高维前缀和也只能做到\(O(n*2^n)\),也就是前面的枚举部分是不合理的。需要一些技巧:因为如果有解的话,合适的模板是超过\((n + 1)/2\)个的方案中的一个,那么找不到一个合法的概率就是\(1/2\)。也就是说第一步可以随机化:随机在序列里选一个作为模板。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
const int N = 2e5+7,M = 65;
char s[N][M];
int main()
{
int n,m,p;scanf("%d%d%d",&n,&m,&p);
forn(i,0,n - 1) scanf("%s",s[i]);
auto begin = chrono::steady_clock::now();
mt19937 rnd(begin.time_since_epoch().count());
ll res = 0;
while((chrono::steady_clock::now() - begin).count() < 2e9)
{
int x = rnd() % n;
vector<int> v;
forn(i,0,m - 1) if(s[x][i] == '1') v.push_back(i);
int sz = v.size();
vector<int> sum(1 << sz);
forn(i,0,n - 1)
{
int mask = 0;
forn(j,0,sz - 1) if(s[i][v[j]] == '1') mask |= (1 << j);
++sum[mask];
}
forn(i,0,sz - 1) forn(S,0,(1 << sz) - 1) if(S >> i & 1) sum[S ^ (1 << i)] += sum[S];
int cur = 0;
forn(S,0,(1 << sz) - 1) if(sum[S] >= (n + 1) / 2 && __builtin_popcount(S) > __builtin_popcount(cur)) cur = S;
if(__builtin_popcount(cur) > __builtin_popcount(res))
{
res = 0;
forn(i,0,sz - 1) if(cur >> i & 1) res |= (1ll << v[i]);
}
}
forn(i,0,m - 1) printf("%lld",res >> i & 1);
return 0;
}