有点久没打cf了,掉分日记。
A. Satisfying Constraints
简单的题意如下图:
结合样例,当第一个数字是1,说明k要大于等于这个数,第一个数字为2则是k小于等于这个数,第一个数字为3则说明不等于这个数。那么我们可以很自然的想出,若是大于等于,则需要大于等于最大的那个值L,若是小于等于,则是小于最小的值R,随后将第三种情况的数字存起来,若是这个数字大于L并且小于R,则要在L-R的区间内减去这些数字的数量,随后输出后即可得到结果,我对情况3的存储采取C++特有容器映射map,大家可以选择学习map或者用数组存储。solve函数用来解决一种情况,随后不再列举主函数。
void solve() {
int n;
cin >> n;
int l = 0,r = 1e10 ;
map<int,int>ma;
while(n--)
{
int a,b;
cin >> a >> b;
if(a == 1)
{
l = max(b,l);
}
else if(a == 2)
{
r = min(b,r);
}
else
ma[b]++;
}
int ans = 0;
if(l > r)
cout << 0 << endl;
else
{
for(auto i : ma)
{
if(i.first >= l && i.first <= r)
ans++;
}
cout << r - l - ans + 1 << endl;
}
}
int main() {
int tt = 1; cin >> tt;
while (tt--) solve();
return 0;
}
B. Summation Game
对这个题我们可以分析一下,当我们是bob,我们会毫不犹豫的尽可能使所有元素都变为负数,以此来让结果尽可能地小。但是如果我们是ALICE,我们就需要考虑这个数字需不需要删除以及自己需要删除几个,例如: 4 2 1 ,1 1 1 9 ,这个时候我们只将9删除,会获得最优解,4 2 1 1 1 1 1 ,这个时候我们就不需要删除就能获得最优解,如此我们可以发现若是要删除,我们删除的往往是最大的数字(这个数字变成负数造成的影响较大),因为我们只需要模拟一遍ALICE删除0~k个数字的情况。同时为了减少计算的次数,我们采取前缀和这一简单算法,可以自行百度了解。无论如何BOB一定会将X个数字或者全部数组变为负数来获得属于BOB的最优解,因此长度一定为X或者N-K的数字需要变为负数。
void solve() {
int n,k,x;
cin >> n >> k >> x;
vector<int>v(n+1),a(n+1);
int sum = 0 ,ans = -1e9;
for(int i = 1 ;i <= n ;i ++)
{
cin >> v[i];
sum+=v[i];
}
sort(v.begin() + 1,v.end(),[&](int x ,int y )
{
return x > y;
});
for(int i = 1 ;i <= n ;i ++)
{
a[i] = v[i] + a[i - 1];
}
for(int i = 0 ;i <= k;i ++)
{
if(i + x > n)
x = n - i;
ans = max(ans, -1 * (a[i + x] - a[i]) + a[n] - a[i + x] );
}
if(k == n)
ans = max(ans,(int)0);
cout << ans << endl;
}
C. Partitioning the Array
这道题在比赛的时候想复杂了,比较无奈,其实思路还是没错的,但是比赛时实现的方法太丑陋导致最后没做出来。首先由题意可知,划分为整数块需要前提是块数是N的因数,因此我们在满足取模为0后进行判断,由分析,若是有1 3 1 1 3 1这种情况,n为6,当因数为3时,则成为 (1 0 1 ) (1 0 1),我们观察一下,可以发现若是差距为 n因数m 的差为0时或者其他数间的差存在公因数时即可获得一分。而c++函数中__gcd函数就可以实现此功能,具体如下:
void solve() {
int n;
cin >> n;
vector<int>v(n);
for(auto &i : v) //迭代器输入
cin >> i;
int sum = 0;
for(int i = 1 ;i <= n ;i ++)
{
if(n % i == 0)
{
int ans = 0;
for(int j = i ;j < n ;j ++)
{
ans = __gcd(ans , abs(v[j] - v[j - i]));
}
if(ans != 1)
sum++;
}
}
cout << sum << endl;
}
D. Array Repetition
当b为1时插入整数x,当b为2时则将前面的数组乘以x倍后加入数组后面(其实就是将数组乘以x+1倍),我们要查询对应下标位置下的数字。那么我们可以在每次操作后都记录操作后最后一个元素的位置,同时,我们要注意当下标大于1e18后就没必要在进行记录了,因为最大的长度为1e18,随后我们进行查询,在查询过程中每次找到左边界和右边界(我的二分中L是大于等于标准值的第一个小标,R是小于标准值的第一个下标),当每次找到的的右边界若是我们在插入过程中插入一个新数字的位置,则成立,若不是则继续二分找到插入新元素的位置(这里我们找到的是次数的下标,类似于反递归处理)
void solve() {
int n,q;
cin >> n >> q;
vector<pair<int,int>>v(n); // 记录大小 pair相当于元素为2的结构体
vector<int>s; // 记录不同情况下的位置
int ans = 0; // 位置
for(int i = 0 ;i < n ;i ++)
{
cin >> v[i].first >> v[i].second;
if(v[i].first == 1)
{
ans++;
s.push_back(ans);
}
else
{
v[i].second++;
if(1e18 / ans < v[i].second) //最大查询为1e18,大于1e18则无需插入
ans = 1e18;
else
ans*=v[i].second; // 说明此时长度为 ans
s.push_back(ans);
}
}
vector<int>a(q);
for(auto &i : a)
cin >> i;
for(int i = 0 ;i < q ;i ++)
{
int k = a[i];
while(1) // 多次二分查找位置
{
int l = 0 , r = s.size() - 1,ans = 0;
while(l <= r)
{
int mid = (l + r) / 2;
if(s[mid] < k)
{
l = mid + 1;
}
else
r = mid - 1;
}
if(v[l].first == 1)
{
cout << v[l].second << " ";
break;
}
else
{
k %= s[r];
if(k == 0)
k = s[r];
}
}
}
cout << endl;
}