写在前面:
这是自己选的一场codeforce 1400-1600 的题。
写写训练日常吧,杜绝自己摸鱼 -- 训练时间 8.9下午
A.Diverse game
解析:
题目保证了矩阵a中的所有数字都是不同的,直接将所有元素往后挪一位,将最后一位挪到第一位即可。
#include <bits/stdc++.h>
using namespace std;
using ll =long long;
const int N=1e5;
inline void solve() {
int n,m; cin >> n >> m;
vector<int>a(n*m);
int cnt=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin >> a[cnt++];
}
}
if(n*m==1) {cout << -1 <<'\n';return;}
int jx=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i==n && j==m) {cout << a[0] <<'\n';return;}
cout << a[jx++] << ' ';
}
cout << '\n';
}
}
int main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int T = 1;
std::cin >> T;
while (T--) solve();
return 0;
}
B. Fun Game
解析:
考虑到,01序列中任意一个字符可以被第一个字符单点修改,那么只要 s 的第一个是‘1’,s就一定可以转换为t,进一步的我们可以发现,后面的数字是可以被前面的数字影响的,而前面的数字最多只能被自己影响,又因为1可以将0变1,1变0,所以我们只需要找到俩个序列的第一个1,判断一下,s的第一个1是不是出现得比 t 早就可以了。特判一下,t 中没有1的情况即可。
#include <bits/stdc++.h>
using namespace std;
using ll =long long;
const int N=1e5;
inline void solve() {
int n; string s,S; cin >> n >> s >> S;
if(s[0]=='1') cout << "YES" << '\n';
else {
int c=-1,d=-1;
for(int i=0;i<n;i++){
if(s[i]=='1' && c==-1) c=i;
if(S[i]=='1' && d==-1) d=i;
if(c!=-1 && d!=-1) break;
}
if(d==-1) cout <<"YES"<<'\n';
else if(c<=d && c!=-1 && d!=-1) cout << "YES"<<'\n';
else
cout << "NO" << '\n';
}
}
int main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int T = 1;
std::cin >> T;
while (T--) solve();
return 0;
}
C. Hungry Games
解析:
正难则反,考虑到所有子区间为 n*(n+1)/2 ,计算出g值为0的区间数目,俩者相减就是答案
双指针扫一遍就好了,注意俩个相邻的区间可以合并。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
ll a[N];
inline void solve() {
//有多少个存在子区间大小刚好超过x的连续子区间
ll n, x;
cin >> n >> x;
for (int i = 1; i <= n; i++) cin >> a[i];
ll sum = 0, ans = 0, cnt = 1; // cnt 为左端点
map<int, int>mp; // 存以端点 i 为右端点的区间和刚好大于x的数目,此时对于另一个紧挨着的以端点 i + 1
// 为左端点的同样一个区间,此时对于答案的贡献就是 mp[i-1] + 1
for (int i = 1; i <= n; i++) {
sum += a[i];
if (sum > x) {
while (sum > x) sum -= a[cnt], ans += mp[cnt - 1] + 1, mp[i] += mp[cnt - 1] + 1, cnt++;
}
}
cout << n*(n + 1) / 2 - ans << '\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T = 1;
std::cin >> T;
while (T--) solve();
return 0;
}
D. Two Movies
解析:
简单模拟题,注意细节贪心求即可
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int A[N], B[N];
inline void solve() {
int n;
cin >> n;
int x = 0, y = 0;
int a = 0, b = 0;
for (int i = 1; i <= n; i++) cin >> A[i];
for (int i = 1; i <= n; i++) cin >> B[i];
for (int i = 1; i <= n; i++) {
int u, v;
u = A[i], v = B[i];
if (u == v) {
if (u == 0) continue;
if (u > 0) a++;
else b++;
} else if (u > v) x += u;
else y += v;
}
if (x < y) swap(x, y);
int k = min(x - y, b);
// cerr << x <<" "<< y;
x -= k;
b -= k;
if (b == 0) {
k = min(x - y, a);
a -= k;
y += k;
if (a == 0) cout << y << '\n';
else cout << x + a / 2 << '\n';
} else {
if (a > b) cout << x + (a - b) / 2 << '\n';
else cout << x - (b - a + 1) / 2 << '\n';
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T = 1;
std::cin >> T;
while (T--) solve();
return 0;
}
E. Mathematical Problem
解析:
插入 n - 2 个运算符,说明至少要合并俩个数字,考虑合并字典序最小的俩个数字,但是注意如果有俩个数字第一个数相同,那么第二个数为1的反而更不优。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5;
inline void solve() {
int n;
cin >> n;
string s;
cin >> s;
if (s.size() <= 2) {
cout << stoi(s) << '\n';
return;
}
if (s[0] == '0' || s[n - 1] == '0') {cout << 0 << '\n';return;}
else if (s.size() >= 4) {
for(int i=1;i<n;i++){
if(s[i]=='0') {cout << 0 << '\n';return;}
}
}
ll res = 0;
string t = "", ss = ""; //最小串,当前串
t += s[0], t += s[1];
ss = t;
for (int i = 2; i < n; i++) {
ss[0]=ss[1];
ss[1]=s[i];
if(t[1]=='1' && ss[0]==t[0]){
t=ss;
}
else {
if(t[0]==ss[0] && ss[1]=='1') continue;
if(ss<t) t=ss;
}
}
int flag=0;
for(int i=0;i<n;i++){
if(i!=n-1 && s[i]==t[0] && s[i+1]==t[1] && !flag) {
int tt=stoi(t);
if(tt!=1) res+=tt;
i++;
flag=1;
}
else if(s[i]=='1'){
continue;
}
else res+=s[i]-'0';
}
if(res==0) res++;
cout << res <<'\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T = 1;
std::cin >> T;
while (T--) solve();
return 0;
}
F. D-Function
解析:注意数据范围,此题需要O(1)去完成,逆元和快速幂不懂的自行百度哦
#include <bits/stdc++.h>
using namespace std;
using ll =long long;
const int N=1e5,p=1e9+7;
ll qpow(ll a,ll x){
ll ans=1;
while(x){
if(x & 1) ans=ans * a %p;
a=a*a%p;
x>>=1;
}
return ans;
}
inline void solve() {
int l,r,k; cin >> l >> r >> k;
l++,r++;
//注意到,对于满足条件的数,最大使用数字 i 满足 k*i<= 9
//所以就是用 0-i 的所有数字 去填 l -- r-1 个空,保证第一个数不为0
ll mx=9/k;
if(mx<1) {
cout << 0 << '\n';
return ;
}
ll ans=0;
// for(int i=l;i<r;i++){
// 分步乘法运算,先确定第一个数有 mx 种放法,后面每一个位置有 mx + 1 种放法
// ans=(ans+qpow(mx+1,i-1)*mx)%p;
// }
// 设 l 位时的贡献是 x, l+1 位的贡献是 x * (mx+1) 等差数列求和
// 答案即为 x * (1- qpow(mx+1,r-l))/(1-mx-1)
// 注意逆元
ans= qpow(mx+1,l-1)*mx%p;
cout << ans*(qpow(mx+1,r-l)-1+p)%p*qpow(mx,p-2)%p << '\n';
}
int main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int T = 1;
std::cin >> T;
while (T--) solve();
return 0;
}
G. Game On Tree
解析:
对于一个只有俩片叶子的树来说,其实就是一个瘦高的“人”字形树,我们首先建树,再判断一下起始点在头还是脚就可以了,这是一个很简单的根据奇偶性的博弈
#include <bits/stdc++.h>
using namespace std;
using ll =long long;
const int N=2e5+10;
vector<int>g[N];
inline void solve() {
int n,t; cin >> n >> t;
for(int i=1;i<n;i++){
int u,v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
map<int,int>mp; //记录每一个节点的儿子的个数
auto dfs=[&](auto self,int x, int pr)->int{
int sum=0;
for(auto i:g[x]){
if(i==pr) continue;
sum+=self(self,i,x)+1;
}
mp[x]=sum;
return sum;
};
dfs(dfs,1,0);
int st; cin >> st;
// 事实上 st 最多只有俩个子树,考虑左子树和右子树的大小即可
int flag=0;
// for(int i= 1;i<=n;i++) cout << mp[i] << ' ';
if(mp[st]+1==n) {
for(auto j:g[st]) if((mp[j]+1) & 1) flag=1;
}
else {
int t=n-mp[st]-1;
if(t & 1) flag=1;
else if(mp[st] & 1) flag=1;
}
if(flag) cout << "Ron";
else cout << "Hermione";
}
int main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) solve();
return 0;
}
H.Turtle vs. Rabbit Race: Optimal Trainings
解析:
对于一个左端点来说,越往右训练的节数肯定是单调增加的,所以我们可以二分找一个分界点,答案就是分界点的左右俩个数中的一个。
#include <bits/stdc++.h>
using namespace std;
using ll =long long;
const int N=1e5+10;
ll a[N],p[N];
inline void solve() {
int n; cin >> n;
for(int i=1;i<=n;i++) {
cin >> a[i];
p[i]=p[i-1]+a[i];
}
int q; cin >> q;
while(q--){
ll l,r=n,u; cin >> l >> u;
// 我们要尽量找到一个以l为起点的子段,使得sum 尽可能的接近 u
// 也就是 a[r + 1] +sum > u && sum - a[r] < u 的分界点
// 我们可以通过预处理前缀和加二分的方式解决这个问题
ll lr=l;
while(lr+1<r){
ll mid = lr+r >> 1;
if(p[mid]-p[l-1]>u) r=mid;
else lr=mid;
}
ll t=p[lr]-p[l-1],tt=p[r]-p[l-1];
ll t1=(u+(u-t+1))*t/2,t2=(u+1)*u/2-(tt-u-1)*(tt-u)/2;
if(t1>=t2) cout << lr << ' ';
else cout << r << ' ';
}
cout << '\n';
}
int main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int T = 1;
std::cin >> T;
while (T--) solve();
return 0;
}
写在最后:
循序渐进,劳逸结合才是王道。