比赛连接:https://codeforces.com/contest/1443
A - Kids Seating
题意:
n个人要坐座位,一共4*n个座位,要求两个人的座位号不能互质,且不能一个座位号能被
另一个人的座位号整除,要求输出n个人的座位号。
思路:
首先输出n*2,然后每次加2,满足公约数是2,且因为最大的是2*n-2,所以也不会出现整除的情况。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
typedef long long ll;
int main()
{
#ifdef LOCAL
freopen("E:/input.txt", "r", stdin);
#endif
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
int num = n * 2;
for (int i = 1; i <= n; i++)
{
cout << num;
num += 2;
printf("%c", i == n ? '\n' : ' ');
}
}
return 0;
}
B - Saving the City
题意:
给出n个位置,每个位置是0或者1,1表示有炸弹,引爆一个炸弹会导致相邻的炸弹爆炸,
两种操作,点爆一个炸弹或者在一个0的位置安防一个炸弹,询问引爆所有炸弹的最小代价。
思路:
设d[i][2]表示引爆第i堆答案是连着上一堆一起点爆的,还是单独点爆的。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
typedef long long ll;
char s[N];
int d[N][2];
int main()
{
#ifdef LOCAL
freopen("E:/input.txt", "r", stdin);
#endif
int t;
cin >> t;
while (t--)
{
int a, b;
cin >> a >> b;
scanf("%s", s + 1);
int len = strlen(s + 1);
int cnt = 0, flag = 0;
ll ans = 0;
int x = 0;
for (int i = 1; i <= len; i++)
{
if (s[i] == '0')
++cnt;
else
{
x++;
d[x][0] = min(d[x - 1][1] + a, d[x - 1][0] + a);
if (x == 1)
d[x][1] = d[x][0];
if (x >= 2)
d[x][1] = min(d[x - 1][1] + cnt * b, d[x - 1][0] + cnt * b);
cnt = 0;
}
}
cout << min(d[x][0], d[x][1]) << endl;
}
return 0;
}
C - The Delivery Dilemma
题意:
给出n个饭店的送饭时间,最开始你可以让一些饭店送到你家,或者你去取
但是你取的饭店时间是相加的,如果是送的话,几家饭店是可以同时送的。
思路:
枚举每个饭店的送餐时间作为最大值的情况,剩余的饭店自己去取,取一个min即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
typedef long long ll;
struct node
{
ll x, y;
bool operator < (const node& oth)const
{
return x < oth.x;
}
}a[N];
ll sum[N];
int main()
{
#ifdef LOCAL
freopen("E:/input.txt", "r", stdin);
#endif
int t;
cin >> t;
while (t--)
{
ll sum = 0;
int n;
scanf("%d", &n);
ll ans = 0;
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i].x), ans = max(ans, a[i].x);
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i].y), sum += a[i].y;
sort(a + 1, a + n + 1);
ans = min(ans, sum);
for (int i = 1; i <= n; i++)
{
ans = min(ans, max(a[i - 1].x, sum));
sum -= a[i].y;
}
cout << ans << endl;
}
return 0;
}
D - Extreme Subtraction
题意:
给出一个数组,两种操作,选某个前缀每个数字减一,或者选某个后缀的每个数字减一,
询问最终能不能把所有数字都变成0。
思路:
如果这个序列是一个递增序列,我们可以仅仅对后缀多次操作,即可全部变成0。
考虑怎么使用前缀的操作使得这个序列满足是递增序列。如果倒着来,当前的比
上一个小,则不用管了,如果大了,说明要使用前缀操作,那么在这个位置以及
之前的位置都要使用前缀操作,这个时候记一个偏移量,如果前面的某个位置减
到负了,说明不满足条件了,就输出NO。若都满足则输出YES。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
typedef long long ll;
int a[N];
int mi1[N];
int mi2[N];
int main()
{
#ifdef LOCAL
freopen("E:/input.txt", "r", stdin);
#endif
int t;
cin >> t;
while (t--)
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
int off = 0, flag = 1;
for (int i = n - 1; i >= 1; i--)
{
a[i] -= off;
if (a[i] < 0)
{
flag = 0;
break;
}
if (a[i] > a[i + 1])
off += a[i] - a[i + 1], a[i] -= a[i] - a[i + 1];
}
puts(flag ? "YES" : "NO");
}
return 0;
}
E - Long Permutation
题意:
有一个长度为n的排列,最开始是1~n,有q次操作,
1号操作询问区间[l,r]的sum,第二种操作是当前排列变换到比其字典序大x的排列。
思路:
询问次数2e5,x最大1e5,一共2e10。
14的阶乘大概8e10,也就是说最多变换后面的14位,我们可以使用逆康拓展开求出当前要求的序列。
然后暴力更新答案就行了,时间复杂度是O(q*(14^2))。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
typedef long long ll;
int a[N];
ll fac[30];
ll sum[N];
void ReContor(int *a, int n, ll k, int ed)
{
int vis[30] = { 0 };
for (int i = 1; i <= n; i++)
{
int cnt = k / fac[n - i], j;
for (j = 1; j <= n; j++) //未使用的数字中第cnt大的
if (!vis[j])
{
if (!cnt) //找到数字
break;
cnt--;
}
vis[j] = 1;
a[i] = ed + j;
k %= fac[n - i]; //对当前阶乘取模
}
}
void init()
{
fac[0] = 1;
for (int i = 1; i <= 20; i++)
fac[i] = fac[i - 1] * i;
}
int main()
{
#ifdef LOCAL
freopen("E:/input.txt", "r", stdin);
#endif
init();
int n, q;
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + i;
int m = 14, ed = n - 14;
if (n < 14)
m = n, ed = 0;
for (int i = 1; i <= m; i++)
a[i] = i;
ll cnt = 0;
while (q--)
{
int op;
scanf("%d", &op);
if (op == 1)
{
int l, r;
scanf("%d%d", &l, &r);
printf("%lld\n", sum[r] - sum[l - 1]);
}
else
{
int x;
scanf("%d", &x);
cnt += x;
ReContor(a, m, cnt, ed);
for (int i = 1; i <= m; i++)
sum[ed + i] = sum[ed + i - 1] + a[i];
for (int i = 1; i <= m; i++)
a[i] = i;
}
}
return 0;
}
F - Identify the Operations
题意:
给出一个长度为n的序列,可以删除a序列的某个数字,然后把左右两个数字任选其一
复制加到空序列b里,给出长度为m的b序列询问有多少种方案满足能得到b序列。
思路:
记录位置,遍历b序列,看下每个数字在a序列左右的数字,如果不是b序列中的,则可以
删除,如果是b序列中的某个数字,但是位置在其之前,因为已经放到b集合了则也可以删除,否
则不能删除,累乘计算答案就可以了。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int mod = 998244353;
typedef long long ll;
int a[N];
int b[N];
int pos1[N];
int pos2[N];
int vis[N];
int main()
{
#ifdef LOCAL
freopen("E:/input.txt", "r", stdin);
#endif
int t;
cin >> t;
while (t--)
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]), pos1[a[i]] = i, vis[i] = 0;
for (int i = 1; i <= m; i++)
scanf("%d", &b[i]), pos2[b[i]] = i, vis[b[i]] = 1;
ll ans = 1;
for (int i = 1; i <= m; i++)
{
int cnt = 0;
if (pos1[b[i]] - 1 >= 1 && (!vis[a[pos1[b[i]] - 1]] || pos2[b[i]] > pos2[a[pos1[b[i]] - 1]]))
cnt++;
if (pos1[b[i]] + 1 <= n && (!vis[a[pos1[b[i]] + 1]] || pos2[b[i]] > pos2[a[pos1[b[i]] + 1]]))
cnt++;
ans = ans * cnt % mod;
}
cout << ans << endl;
}
return 0;
}