Codeforces Round #719 (Div. 3) 题解
https://codeforces.com/contest/1520
在B题上卡了一下,我是SB。
A题
题意:
就问你有没有字母不是连续着出现
思路:
直接判断即可
string s;
int n;
int cnt[26];
int main(){
int T;
cin >> T;
while(T--)
{
memset(cnt, 0, sizeof cnt);
cin >> n;
cin >> s;
bool flag = true;
for(int i = 0 ; i < n ; i ++)
{
if(i && s[i - 1] != s[i])
if(cnt[s[i] - 'A'])
{
flag = false;
break;
}
cnt[s[i] - 'A'] ++;
}
if(flag) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}
B题
题意:问你从1到n要多少个数是数的每位数字都相等
思路:
直接枚举相同的数字是\(X_i\),然后不断进行\(X_i = X_i * 10 + X_i\)操作,直到数字大于n,统计数目即可。
ll n, k;
int main(){
IOS;
int T;
cin >> T;
while(T--)
{
cin >> n;
ll ans = 0;
for(ll i = 1 ; i <= 9 ; i ++)
{
ll temp = 0;
while(temp * 10 + i <= n)
{
temp = temp * 10 + i;
ans ++;
}
}
cout << ans <<"\n";
}
return 0;
}
开始错误的代码
这个写法错在了特判最高位是否可行处
ll n;
vector<int> v;
int main(){
IOS;
int T;
cin >> T;
while(T--)
{
cin >> n;
while(n)
{
v.push_back(n % 10);
n /= 10;
}
reverse(v.begin(), v.end());
int ans = (v.size() - 1) * 9 + v[0] - 1;
bool flag = true;
int d = v[0];
for(auto x : v) //此处错了
if(x < d) flag = false;
if(flag) ans ++;
cout << ans << "\n";
v.clear();
}
return 0;
}
C题
题意:让你将 n * n个数放进 n * n的矩阵里,使得相邻位置的数数值不相邻
思路:
按照顺序,先放奇数,再放偶数,最后判断是否合法即可。
int n;
int g[N][N];
int dx[] = {1, 0, -1, 0};
int dy[] = {0, 1, 0, -1};
bool check(int a, int b)
{
int c = g[a][b];
for(int i = 0 ; i < 4 ; i ++)
{
int x = a + dx[i];
int y = b + dy[i];
if(x < 1 || x > n || y < 1 || y > n) continue;
if(abs(g[x][y] - c) == 1) return false;
}
return true;
}
int main(){
IOS;
int T;
cin >> T;
while(T--)
{
cin >> n;
int lim = n * n;
int l = 1, r = 2;
for(int i = 1 ; i <= n ; i ++)
for(int j = 1 ; j <= n ; j ++)
{
if(l <= lim) g[i][j] = l, l += 2;
else g[i][j] = r, r += 2;
}
bool flag = true;
for(int i = 1 ; i <= n ; i ++)
{
if(!flag) break;
for(int j = 1 ; j <= n ; j ++)
{
if(!check(i, j)) flag = false;
break;
}
}
if(!flag) cout << "-1\n";
else
{
for(int i = 1 ; i <= n ; i ++)
{
for(int j = 1 ; j <= n ; j ++)
cout << g[i][j] << ' ';
cout << '\n';
}
}
}
return 0;
}
D题
题意:
给你一个序列,问你有多少序列满足\(i < j\)并且$ a_j - a_i = j - i $。
思路:
将式子变形,变成$ a_j - j = a_i - i $,就非常简单啦
int n;
int a[N];
map<ll, ll> mp;
int main(){
IOS;
int T;
cin >> T;
while(T--)
{
cin >> n;
for(int i = 1 ; i <= n ; i ++)
{
cin >> a[i];
a[i] -= i;
mp[a[i]] ++;
}
ll ans = 0;
for(auto x : mp) ans += x.y * (x.y - 1) / 2;
cout << ans << "\n";
mp.clear();
}
return 0;
}
E题
题意:给你一个字符串,里面有 . 和 * ,每次可以移动一个到.处,问你最少移动多少次可以让所有连续。
思路:
直接线性dp即可,因为你左边怎么凑过来的和右边怎么凑过来的无关,只需要求左边加右边最小即可。
int n;
string s;
pll l[N];
pll r[N];
int main(){
IOS;
int T;
cin >> T;
while(T--)
{
cin >> n >> s;
l[0].y = (s[0] == '*');
for(int i = 1 ; i < n ; i ++)
{
if(s[i] != '*')
{
l[i].x = l[i - 1].x + l[i - 1].y;
l[i].y = l[i - 1].y;
}
else l[i] = l[i - 1];
if(s[i] == '*')
l[i].y ++;
}
r[n - 1].y = (s[n - 1] == '*');
for(int i = n - 2 ; i >= 0 ; i --)
{
if(s[i] != '*')
{
r[i].x = r[i + 1].x + r[i + 1].y;
r[i].y = r[i + 1].y;
}
else r[i] = r[i + 1];
if(s[i] == '*')
r[i].y ++;
}
ll ans = 1e18;
for(int i = 0 ; i < n ; i ++)
ans = min(ans, l[i].x + r[i].x);
cout << ans << "\n";
for(int i = 0 ; i < n ; i ++)
l[i].x = l[i].y = r[i].x = r[i].y = 0;
}
return 0;
}
F1题
题意:
有一个隐藏的数组,只有0和1,让你每次查询一个区间,每次系统会返回区间的和,让你在20次查询内找到数组中的第k个0。
思路:
显然是二分,二分查找即可,详情看如下代码。
int n, t, k;
int sum;
int main(){
IOS;
cin >> n >> t;
cin >> k;
int l = 1, r = n;
while(l < r)
{
int mid = l + r >> 1;
cout << "? " << l << " " << mid << "\n";
cout.flush();
cin >> sum;
if(sum == 0)
{
cout << "! " << l + k - 1 << "\n";
cout.flush();
return 0;
}
if(sum + k <= mid - l + 1) r = mid;
else k -= (mid - l + 1 - sum), l = mid + 1;
}
cout << "! " << l << "\n";
cout.flush();
return 0;
}
F2题
这题懒得写了,转载自我儿子的博客
题意:
交互(复杂版本),再F1的基础上多了几次需要查询的k值,并且每次查询之后需要将该处的0变为1,询问的上界为\(6∗10^4\)。
思路:
根据F1查询的思路,发现我们是需要使用到前i个数中1出现的次数,也就是前缀和,那么我们可以首先依次询问[1,R],(1≤R≤n)然后维护一下前缀和,再对每个查询用二分查找。
但是这样由于\(n≤2∗10^5\),如果依次询问必定会超过上界,因此可以将区间[1,n]分块,维护一下分块的前缀和,然后对每个边界二分询问即可。设分块大小为x,则\(nx+m∗logx≤6∗10^4\),化简之后得\(20x+logx≤6\),解得近似\(5≤x≤30\),对时间复杂度进行分析之后发现取\(x=30\)最好。
注意:我们维护的是分块边界,内部并没有维护,因此k需要减去的是边界处对应的值。
const int N = 200100;
int tr[N];
int n, m, k;
int lowbit(int x)
{
return x & -x;
}
int ask(int x)
{
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
void add(int x, int d)
{
for (int i = x; i <= n; i += lowbit(i)) tr[i] += d;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i ++ )
{
cin >> k;
if (i == 1)
{
int sum1 = 0, sum2 = 0;
for (int i = 1; i < n; i += 30)
{
cout << "? " << 1 << " " << i << endl;
cout.flush();
cin >> sum2;
add(i, sum2 - sum1);
sum1 = sum2;
}
cout << "? " << 1 << " " << n << endl;
cout.flush();
cin >> sum2;
add(n, sum2 - sum1);
}
int l = 1, r = n;
for (int i = 1; i < n; i += 30)
if (i - ask(i) >= k)
{
l = max(1, i - 30) + 1;
r = i;
k -= l - 1 - ask(l - 1);
break;
}
while (l < r)
{
int sum;
int mid = l + r >> 1;
cout << "? " << l << " " << mid << endl;
cout.flush();
cin >> sum;
if (mid - l + 1 - sum >= k) r = mid;
else k -= mid - l + 1 - sum, l = mid + 1;
}
cout << "! " << r << endl;
cout.flush();
add(r, 1);
}
cin >> k;
return 0;
}
G题
题意:
给你一个网格图,含有有障碍物和传送门,走到相邻格和传送都有一定的花费,传送门可以到达其它任意传送门,问你从起到到终点的最小花费是多少。
思路:
由于传送门的数量巨大,我们不可能一对一对枚举传送门,因此采用双向处理,最后再取一下min(ans, l + r),其中l + r是从两边使用传送门的最小花费
int n, m, worth;
int g[N][N];
int arr[N][N], cnt;
vector<pii> v;
vector<int> s[3];
ll dist1[M], dist2[M];
int val[M];
bool st[M];
int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};
void bfs(int type, ll dist[])
{
memset(st, 0, sizeof st);
queue<pll> q;
if(type == 1) dist[1] = 0, q.push({1, 1});
else dist[n * m] = 0, q.push({n, m});
while(q.size())
{
auto t = q.front();
q.pop();
int x = t.x, y = t.y;
if(g[x][y] > 0) s[type].push_back(arr[x][y]);
if(st[arr[x][y]]) continue;
st[arr[x][y]] = true;
if(g[x][y] == -1) continue;
for(int i = 0 ; i < 4 ; i ++)
{
int a = x + dx[i], b = y + dy[i];
if(a < 1 || a > n || b < 1 || b > m) continue;
if(g[a][b] == -1 || st[arr[a][b]]) continue;
dist[arr[a][b]] = dist[arr[x][y]] + worth;
q.push({a, b});
}
}
}
int main()
{
IOS;
cin >> n >> m >> worth;
for(int i = 1 ; i <= n ; i ++)
{
for(int j = 1 ; j <= m ; j ++)
{
cin >> g[i][j];
arr[i][j] = ++ cnt;
val[cnt] = g[i][j];
}
}
memset(dist1, 0x3f, sizeof dist1);
memset(dist2, 0x3f, sizeof dist2);
bfs(1, dist1);
bfs(2, dist2);
ll res = min(dist1[n * m], dist2[1]);
ll l = 1e18, r = 1e18;
for(auto x : s[1]) l = min(l, dist1[x] + val[x]);
for(auto x : s[2]) r = min(r, dist2[x] + val[x]);
res = min(res, l + r);
if(res >= 1e18) res = -1;
cout << res << endl;
return 0;
}

浙公网安备 33010602011771号