A
看了大佬们的做法都是比较复杂的,jiangly神写的很简洁,可以学习一下,还有换行的技巧。
#include <bits/stdc++.h> using i64 = long long ;void solve () { int n, m; std::cin >> n >> m; int N = n * m; for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < m; j++) { int x; std::cin >> x; if (N == 1 ) { x = -1 ; } else if (x == N) { x = 1 ; } else { x++; } std::cout << x << " \n" [j == m - 1 ]; } } } int main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); int t; std::cin >> t; while (t--) { solve (); } return 0 ; }
B
容易发现:只需要在\(s_i=0,t_i=1\) 的情况下,判断\(s_i\) 前面有没有1就行,可以使用一个前缀和统计一下。
#include <bits/stdc++.h> using namespace std;using ll=long long ;const int MAXN=2e5 +10 ;void solve () { int n; cin>>n; string s,t; cin>>s>>t; std::vector<int > cnt (n,0 ) ; if (s[0 ]=='1' ) cnt[0 ]=1 ; for (int i=1 ;i<n;i++) { cnt[i]=cnt[i-1 ]+s[i]-'0' ; } for (int i=0 ;i<n;i++) { if (s[i]=='0' && t[i]=='1' ) { if (!cnt[i]) { cout<<"NO\n" ; return ; } } } cout<<"YES\n" ; } int main () { int t=1 ; cin>>t; while (t--) solve (); return 0 ; }
看完jiangly神的码,总是感觉自己很蠢,看大佬的码还是大有裨益。
#include <bits/stdc++.h> using i64 = long long ;void solve () { int n; std::cin >> n; std::string s, t; std::cin >> s >> t; bool ok = true ; for (int i = 0 ; i < n; i++) { if (s[i] == '1' ) { break ; } if (t[i] == '1' ) { ok = false ; break ; } } if (ok) { std::cout << "YES\n" ; } else { std::cout << "NO\n" ; } } int main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); int t; std::cin >> t; while (t--) { solve (); } return 0 ; }
C
直接暴力肯定会TLE,不难想到:可以使用前缀和先计算各个区间的值。
考虑一个合法的区间\(a_l-a_{p-1}\) ,那么\(a_{p-1}\) 之后(从\(a_p\) 开始)就是重置为0的情况。不难发现:如果是在\(a_{l}-a_{p-1}\) 之间,答案就是\(p-l-1\) ,那么在\(a_{p-1}\) 之后呢?就是\(a_p\) 合法的情况(类似的推导),所以这里可以使用\(dp\) ,不难得到状态转移方程为\(dp_i=dp_p+p-1-i\) 。(这里\(dp_i\) 表示的就是从\(i\) 位置以后找到的合法情况)。
至于找\(p\) ,可以用二分查找,具体看代码。
#include <bits/stdc++.h> using namespace std;using ll=long long ;const int MAXN=2e5 +10 ;const ll mod=998244353 ;void solve () { ll n; ll m; cin>>n>>m; std::vector<ll> v (n+1 ,0 ) ; for (int i=1 ;i<=n;i++) { cin>>v[i]; } std::vector<ll> sum (n+1 ,0 ) ; for (int i=1 ;i<=n;i++) sum[i]=sum[i-1 ]+v[i]; std::vector<ll> dp (n+2 ,0 ) ; for (int i=n-1 ;i>=0 ;i--) { int p=upper_bound (sum.begin ()+i,sum.end (),sum[i]+m)-sum.begin (); dp[i]=dp[p]+p-i-1 ; } cout<<accumulate (dp.begin (),dp.end (),0ll )<<"\n" ; } int main () { int t=1 ; cin>>t; while (t--) solve (); return 0 ; }
下面是双指针的做法,这里\(dp\) 数组维护的是从\(a_i\) 后,一直到\(a_{n-1}\) 之间,不合法的区间的长度(包含本身,所以基本长度就是1,所以\(dp_n=1\) ),先用双指针寻找合法的区间,再进行\(dp\) 。
解释一下状态转移方程:\(a_i\) 到\(a_{j-1}\) 为合法的区间, 那么从\(dp_j\) 往后开始就是非法的区间了,所以状态转移方程是\(dp_i=dp_j+1\) ,所以不难得到此时非法的范围数值就是\(dp_j\) ,这里用\(dp_i-1\) 主要是因为需要讨论\(j\) ,写成这样简单一点。
#include <bits/stdc++.h> using i64 = long long ;void solve () { int n, x; std::cin >> n >> x; std::vector<int > a (n) ; for (int i = 0 ; i < n; i++) { std::cin >> a[i]; } std::vector<i64> s (n + 1 ) ; for (int i = 0 ; i < n; i++) { s[i + 1 ] = s[i] + a[i]; } i64 ans = 1LL * n * (n + 1 ) / 2 ; std::vector<int > dp (n + 1 ,0 ) ; dp[n] = 1 ; for (int i = n - 1 , j = n + 1 ; i >= 0 ; i--) { while (s[j - 1 ] - s[i] > x) { j--; } dp[i] = 1 + (j <= n ? dp[j] : 0 ); ans -= dp[i] - 1 ; } std::cout << ans << "\n" ; } int main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); int t; std::cin >> t; while (t--) { solve (); } return 0 ; }
D
容易想到是并查集,合并的条件是\(|a_u-a_v|\%x=0\) ,不难想到\(a_u=k_1x+b,a_v=k_2x+b\) ,我们执行\(n\) 次操作,对应就是\(x:1-n\) ,我们可以通过枚举\(x\) ,再枚举\(a_u\) 进行合并,\(a_v\) 怎么处理?不妨让\(a_v\) 直接为\(a\%x\) ,也就是\(b\) 即可。通过一个数组先存起来,直到寻找到满足条件的即可。
关键的是\(x\) 需要从\(n-1\) 开始倒序枚举,这里是利用了抽屉原理,想一下:如果当前有\(x\) 个点,然后所有数都取模于\(x-1\) ,那么他们的范围一定是在\(0-(x-2)\) 之间,说明一定会有两个点取模后存在相同的值,这样可以保证每一次操作都能连接两个不同的集合,故必定有解。
#include <bits/stdc++.h> using namespace std;using ll=long long ;const ll mod=1e9 +7 ;void solve () { int n; cin>>n; std::vector<ll> a (n,0 ) ; for (auto &t: a) cin>>t; std::vector<int > fa (n,0 ) ; auto init=[&](int n)->void { for (int i=0 ;i<n;i++) fa[i]=i; }; auto find=[&](auto && self,int x)->int { if (fa[x]==x) return x; return fa[x]=self (self,fa[x]); }; auto merge=[&](int x,int y,auto && find)->void { int fx=find (find,x),fy=find (find,y); if (fx!=fy) fa[fx]=fy; }; init (n); std::vector<int > u (n) ; std::vector<int > v (n) ; for (int x=n-1 ;x>=1 ;x++) { std::vector<int > tmp (x,-1 ) ; for (int i=0 ;i<n;i++) { if (find (find,i)!=i) continue ; if (tmp[a[i]%x]!=-1 ) { u[x]=tmp[a[i]%x]; v[x]=i; merge (u[x],v[x],find); break ; } else tmp[a[i]%x]=i; } } cout<<"YES\n" ; for (int i=1 ;i<n;i++) { cout<<u[i]+1 <<" " <<v[i]+1 <<"\n" ; } } int main () { int t=1 ; cin>>t; while (t--) solve (); return 0 ; }