本场链接:Codeforces Round #724 (Div. 2)
A. Omkar and Bad Story
通过样例会发现只要有负数就会无解,因为最大的数一旦拿去减最小的数就会出现更大的数,更大的数又可以跟负数凑出更大的数,无法构造。只有非负数的时候不难想到直接把所有数填入即可。
#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 = 105;
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]);
bool neg = 0;
forn(i,1,n) if(a[i] < 0) neg = 1;
if(neg)
{
puts("NO");
continue;
}
puts("YES");
puts("101");
forn(i,0,100) printf("%d ",i);
puts("");
}
return 0;
}
B. Prinzessin der Verurteilung
不难猜到按题目要求的MEX
的大小不会很大,直接暴力查即可。
#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 n;
string s;
string bfs()
{
queue<string> q;
forn(i,0,25)
{
char ch = 'a' + i;
string _;_.push_back(ch);
q.push(_);
}
while(!q.empty())
{
string u = q.front();q.pop();
if(s.find(u) == string::npos) return u;
forn(i,0,25)
{
char ch = 'a' + i;
u.push_back(ch);
q.push(u);
u.pop_back();
}
}
}
int main()
{
Angel_Dust;
int T;cin >> T;
while(T--)
{
cin >> n >> s;
cout << bfs() << endl;
}
return 0;
}
C. Diluc and Kaeya
首先考虑一个dp
:
- 状态:(f[i])表示前(i)个元素分割的方案数
- 入口:(f[i] = 1),因为自己自成一段是被允许的。
- 转移:枚举最后一段的左端点(k),划分成两个区间:([1,k])和([k + 1,n]),由于题目只需要成比例就可以了,那么不难想到如果整个([1,k])不与([k+1,n])成比例的话,无论怎么拆分([1,k])都不会出现一个合理的解,因为把整个区间加起来不是成比例的话就矛盾了。所以只需要判断这两个区间是否是成比例的就可以判断是否可以划分了,那么现在的问题等价于求(j)的数量:(j in [1,n - 1])且满足([1,j])与([j + 1,n])成比例,如果按分数进行查询的话,会有
double
的精度问题肯定不合理,不妨直接把分子分母存成一个pii
,但是这里需要把所有成比例的加入答案,显然也不可能去枚举比例再查询对应的数量,但是既然成比例,就说明最简形式是相同的:让分子分母同分再查询对应的数量就可以了。 - 出口:(f[i])对应第(i)个答案。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#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)
#define x first
#define y second
const int N = 5e5+7;
int sumD[N],sumK[N],f[N];
char s[N];
int gcd(int x,int y)
{
if(y == 0) return x;
return gcd(y,x % y);
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n;scanf("%d",&n);
forn(i,1,n) f[i] = 0;
scanf("%s",s + 1);
forn(i,1,n)
{
sumD[i] = sumD[i - 1] + (s[i] == 'D');
sumK[i] = sumK[i - 1] + (s[i] == 'K');
}
map<pii,int> st;
forn(i,1,n)
{
pii cur = {sumD[i] / gcd(sumD[i],sumK[i]),sumK[i] / gcd(sumD[i],sumK[i])};
++st[cur];
printf("%d ",st[cur]);
}
puts("");
}
return 0;
}
D. Omkar and Medians
直接按题目的要求模拟:显然首先有(a[1] = b[1]),直接放入就可以了。不难发现后面的过程就是每次插入两个数,使整个数组的中位数使某个要求的值。
对于构造(b[i])成为中位数的过程:首先设还需要插入的数的个数是(k),特别地,如果一开始(b[i])并不在当前的(a)里面,那么需要首先消耗一次插入机会把(b[i])放进来。其次求出(>b[i])和(<b[i])的数的数量,分别记为(up,dw)。由于这样的构造保证每个(b[i])只存在一个,所以可以不用管可能有多个(b[i])的情况。再者,如果(up == dw)即(b[i])已经是整个序列的中位数了,那么首位各插入一个正负无穷大,就可以保证中位数还是(b[i])了。反之如果(|up - dw| eq k),说明无解,否则的话按(up)与(dw)的大小关系插入正负无穷大来使(b[i])移动到中位数的位置即可。
那么为了查询(b[i])与整个数组中其他数的大小关系,写一个维护值域上元素个数的数据结构即可,代码中实现的是离散化后维护BIT
。注意本题是多测,每次操作之后需要把BIT
清空,需要写一些额外的代码来记录整个BIT
的状态完成清空。
但是本题让使用memset
清空的代码跑过了,可能是没人hack
,原则上应该是会被卡的。
#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;
int c[N],b[N];
vector<int> val;
inline int lowbit(int x)
{
return x & -x;
}
void modify(int x,int v)
{
for(int i = x;i < N;i += lowbit(i))
c[i] += v;
}
int query(int x)
{
int res = 0;
for(int i = x;i;i -= lowbit(i))
res += c[i];
return res;
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
val.clear();
int n;scanf("%d",&n);
forn(i,1,n) scanf("%d",&b[i]),val.push_back(b[i]);
sort(val.begin(),val.end());val.erase(unique(val.begin(),val.end()),val.end());
forn(i,1,n) b[i] = lower_bound(val.begin(),val.end(),b[i]) - val.begin() + 2;
val.clear();
bool ok = 1;
int cneginf = 0,cposinf = 0;
modify(b[1],1);val.push_back(b[1]);
forn(i,2,n)
{
int k = 2;
if(query(b[i]) - query(b[i] - 1) == 0) modify(b[i],1),--k,val.push_back(b[i]);
int dw = query(b[i] - 1),up = query(N - 1) - query(b[i]);
if(up == dw && k == 2)
{
++cneginf;++cposinf;
modify(N - 1,1);modify(1,1);
continue;
}
if(abs(up - dw) != k)
{
ok = 0;
break;
}
forn(_,1,k) if(up > dw) modify(1,1),++cneginf;else modify(N - 1,1),++cposinf;
}
if(ok) puts("YES");
else puts("NO");
for(auto& v : val) modify(v,-1);
forn(_,1,cneginf) modify(1,-1);
forn(_,1,cposinf) modify(N - 1,-1);
}
return 0;
}
E. Omkar and Forest
每个#
号有两种情况:要么填0
要么填一个非0
正整数。
猜想:如果事先规定某些#
为0
,其他的都不可以再使用0
,则整个局面是唯一的。
证明:
- 构造一种可行方案:从所有
0
出发做BFS
,每个#
的值是距离自己最近的0
的距离。 - 证明这个可行方案唯一,等价于证明没有某一个非
0
正整数可以再+1
。假如身边有一个0
显然就会产生矛盾,如果没有,那么因为必须要和身边的数差距不超过(1),身边的数必须也因此增加,这样一定会导致某一个(0)旁边的数产生矛盾。所以这样构造出来的可行方案是唯一的。
所以每个#
各自都有两种方案,(ans = 2^k)其中(k)是#
的数量
#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 = 2005,MOD = 1e9+7;
char s[N][N];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n,m;scanf("%d%d",&n,&m);
forn(i,1,n) scanf("%s",s[i] + 1);
ll res = 1;
bool have = 0;
forn(i,1,n) forn(j,1,m)
{
if(s[i][j] == '0') have = 1;
else res = res * 2ll % MOD;
}
if(!have) res = ((res - 1) % MOD + MOD) % MOD;
printf("%lld
",res);
}
return 0;
}