Codeforces Round #533(div.2)解题报告
A. Salem and Sticks
题意:
- 给定一个序列长度为n,你可以对每个数字加或者减。
- 每次加减的代价是数字大小变化之差。
- 序列中一个数字如果和一个数字t绝对值之差小于等于一,认为这个数字是好的。
请你给出一个t,并求出使序列所有数字变化后是好的最小代价。
数据范围\(n\leq 1000,a_i\leq 100\)。
思路:
- 因为数据范围比较小,所以直接枚举\(t\),每次线性判断结果,取最小值。
#include
using namespace std;
const int maxn = 1000 + 10;
int n, a[maxn];
int main()
{
scanf("%d", &n);
int l = 0x3f3f3f3f, r = -1;
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
l = min(l, a[i]);
r = max(r, a[i]);
}
int ans_t = 0;
int ans_s = 0x3f3f3f3f;
for(int t = l; t <= r; t++)
{
int sum = 0;
for(int i = 1; i <= n; i++)
{
int x = a[i];
if(abs(x-t) <= 1) continue;
if(x >= t) sum += x - (t+1);
else sum += (t-1) - x;
}
if(sum < ans_s)
{
ans_t = t;
ans_s = sum;
}
}
cout << ans_t << " " << ans_s << endl;
return 0;
}
B. Zuhair and Strings
题意:
- 给一串长度为n的字符串,求长度为k的相同单种字母组成的子串的最大数量。
思路:
- 暴力判断就行。因为只有一个字符,也用不上什么算法。
#include
using namespace std;
const int maxn = 2e5 + 10;
int n, k;
string str;
int a[30];
bool cmp(int a, int b){
return a > b;
}
int main()
{
cin >> n >> k;
cin >> str;
int res = 1;
for(int i = 0; i < n; i++)
{
if(k == 1) a[str[i]-'a']++;
else
{
//连续出现的字符
if(str[i] == str[i+1]) res ++;
else res = 1;
//出现了k次
if(res == k)
{
a[str[i]-'a']++;
res = 1; i += 1; //跳过i+1这个字符
}
}
} int ans = 0;
for(int i = 0; i <= 28; i++) ans = max(ans, a[i]);
cout << ans << endl;
return 0;
}
C. Ayoub and Lost Array
题意:
- 一个长度为n的序列,每个数字在\([l,r]\)区间。
- 序列数字的和是3的倍数。
- 问有多少序列满足条件。
- 对结果取模\(10^9+7\)。
思路:
如果要考虑数字的话,那也太麻烦了。
- 数的总和只有三种关系,\(sum\ mod\ 3=1,2,0\),可以从这个方向考虑。
- 设\(f(i,0/1/2)\)表示长度为\(i\)的序列中,总和取模3为0,1,2的序列的数量。
先处理一下区间模3余0,1,2的数量,然后推一推公式就行了。
#include
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int maxn = 2e5 + 10;
int n, l, r, m0, m1, m2;
ll f[maxn][5];
void init()
{
m0 += r/3, m1 += r/3, m2 += r/3;
m0 -= (l-1)/3, m1 -= (l-1)/3;
m2 -= (l-1)/3;
int t = r%3;
if(t == 1) m1++;
else if(t == 2) m1++, m2++;
t = (l-1) % 3;
if(t == 1) m1--;
else if(t == 2) m1--, m2--;
}
int main()
{
cin >> n >> l >> r;
init();
f[1][0] = m0, f[1][1] = m1, f[1][2] = m2;
for(int i = 2; i <= n; i++)
{
f[i][0] += (f[i-1][0]*m0)%mod; f[i][0] %= mod;
f[i][0] += (f[i-1][1]*m2)%mod; f[i][0] %= mod;
f[i][0] += (f[i-1][2]*m1)%mod; f[i][0] %= mod;
f[i][1] += (f[i-1][1]*m0)%mod; f[i][1] %= mod;
f[i][1] += (f[i-1][2]*m2)%mod; f[i][1] %= mod;
f[i][1] += (f[i-1][0]*m1)%mod; f[i][1] %= mod;
f[i][2] += (f[i-1][1]*m1)%mod; f[i][2] %= mod;
f[i][2] += (f[i-1][2]*m0)%mod; f[i][2] %= mod;
f[i][2] += (f[i-1][0]*m2)%mod; f[i][2] %= mod;
}
cout << f[n][0] << endl;
return 0;
}
D. Kilani and the Game
题意:
- 几个人在玩一个游戏,首先有一个二维棋盘。
- 1,2,3,...号玩家轮流移动,每次可以从已有的地盘开始向上下左右扩张占领,扩张的速度是\(s(i)\)。
- 不能占领被其他玩家占领或者被锁住的地盘,当没有玩家能够移动的时候,游戏结束。
- 问最终这几名玩家每人占领多少地盘?
思路:
- 首先,对于每个玩家扩张而言,就是bfs。
- 每个玩家扩张的速度是\(s(i)\),其实意思就是bfs搜的深度是\(s(i)\)。
- 我们有\(p\)个玩家,\(p\leq 9\),其实可以直接维护\(p\)个队列来进行bfs。
- 我们知道bfs具有两段性和单调性。
- 两段性:bfs队列中只有两层节点。
- 单调性:bfs搜完一层才会搜下一层。
- 所以我们可以借助这两个性质完成对深度的控制。
- 同时当没有人能够扩展节点后,退出游戏,输出结果。
#include
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 10;
int n, m, p;
int s[15];
int a[maxn][maxn];
char t[maxn][maxn];
struct Node{
int x, y, step;
};
queue q[15], qt;
int dx[5] = {0, -1, 1, 0, 0};
int dy[5] = {0, 0, 0, -1, 1};
int ans[12];
void bfs(int u)
{
while(qt.size())
{
Node tmp = qt.front(); qt.pop();
if(tmp.step == 0) q[u].push(tmp);
else
{
for(int i = 1, x, y; i <= 4; i++)
{
x = tmp.x + dx[i];
y = tmp.y + dy[i];
if( (x < 1 || x > n || y < 1 || y > m) || a[x][y] != 0 || t[x][y] != '.') continue;
a[x][y] = u;
qt.push(Node{x, y, tmp.step-1});
}
}
}
}
bool solve(int x)
{
while(q[x].size())
{
auto u = q[x].front();
q[x].pop();
u.step = s[x];
qt.push(u);
}
bfs(x);
return q[x].size();
//队列中还有 说明还能继续扩展
}
int main()
{
scanf("%d%d%d", &n, &m, &p);
for(int i = 1; i <= p; i++)
scanf("%d", &s[i]);
for(int i = 1; i <= n; i++)
{
scanf("%s", t[i]+1);
for(int j = 1; j <= m; j++)
if(t[i][j] >= '1' && t[i][j] <= '9')
a[i][j] = t[i][j] - '0';
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
if(a[i][j] != 0 && a[i][j] != -1)
q[a[i][j]].push(Node{i, j, s[a[i][j]]});
}
while(1)
{
bool flag = 0;
for(int i = 1; i <= p; i++)
flag |= solve(i);
if(!flag) break;
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
if(a[i][j] != -1 && a[i][j] != 0)
ans[a[i][j]]++;
}
for(int i = 1; i <= p; i++)
printf("%d ", ans[i]);
return 0;
}
E. Helping Hiasat
- 最大独立集板题,待补。