本文最后更新于 2025年2月17日 晚上
codeforces round 1005(Div.2)
https://codeforces.com/contest/2064
https://codeforces.com/blog/entry/138912
A 采用模拟,a字符串从头遍历,找到第一个1,它和其后的字符串删除增加到b字符串后面;b数组从头遍历,找到第一个0;
应该使用数组比较简单;条件怎么判断
请注意,如果字符串 s 以字符 1 开头,那么在某个时刻,整个字符串 s 必须被移动到字符串 t 中。同时,如果执行操作,那么在两个字符串中,”01” 和 “10” 的出现次数最多只能减少一次。这给出了答案的上界,即 s 中 “01” 和 “10” 的出现次数,如果 s 以字符 1 开头,则在此基础上加 1。
接下来,以下构造方式使用的操作次数与上述上界相同(从而证明了这就是最小操作次数):如果 s 以字符 1 开头,那么选择整个字符串 s 并将其移动到 t 中。然后,反复查找 s 或 t 中第一个与前一个字符不同的字符(注意在这种构造中,这样的索引一次只能存在于一个字符串中),并选择从这个字符开始的后缀,并将其移动到另一个字符串中。在这个构造过程中,s 的某个前缀将包含字符 0,而 t 的某个前缀将包含字符 1,因此每次移动后,”01” 和 “10” 的总数都会减少。
因此,这个问题的答案就是 s 中 “01” 和 “10” 的出现次数,如果 s 以字符 1 开头,则在此基础上加 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <bits/stdc++.h> using namespace std;\void solve () { int n;cin>>n; string s;cin>>s; int ans=0 ; if (s[0 ]=='1' ) ans++; for (int i=0 ;i<n-1 ;i++) { if (s[i]!=s[i+1 ]) ans++; } cout<<ans<<'\n' ; }int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); int tt;cin>>tt; while (tt--) { solve (); } return 0 ; }
B 删除子数组使得相同数字尽可能多;条件怎么设置,没有思路
只删除一个数字,个数减1而不重复数字最多减1,不会使分数增加。所以要使分数增加,删除最长的不重复数字组成的子数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <bits/stdc++.h> using namespace std;void solve () { int n;cin>>n; vector<int > a (n) ; vector<int > cnt (n) ; for (int i=0 ;i<n;i++) { cin>>a[i]; a[i]--; cnt[a[i]]++; } int l=0 ,r=0 ; int len=0 ; for (int i=0 ;i<n;i++) { if (cnt[a[i]]>1 ) { len=0 ; } else { len++; } if (len>r-l) { r=i+1 ; l=i+1 -len; } } if (l==r) { cout<<0 <<'\n' ; } else { cout<<l+1 <<' ' <<r<<'\n' ; } }int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); int tt;cin>>tt; while (tt--) { solve (); } return 0 ; }
C ai小于0删除后缀,大于0删除前缀
如果数组左边都是大于0的数,右边都是小于0的数,很好处理
但是左边小于0,右边大于0呢
进一步,分布没有规律,分成好多子数组怎么办
首先,要注意在任何情况下,我们应该要么移除最左边的正数,要么移除最右边的负数。因为如果我们选择一个不是最左边的正数,那么我们本可以先选择最左边的正数,从而获得更高的分数。对于选择最右边的负数,也有类似的论证。
现在,如果你执行这些操作若干次,你最终总是会取走一些正数的前缀,然后是剩余的负数的后缀。因此,为了计算答案,我们只需要检查所有 n+1 种将数组分割成前缀和后缀的方式,并取其中的最大值,这可以在 O(n) 时间内轻松完成。
遍历,计算前缀和和后缀和,相加即为当前位置的分数,选出最大值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <bits/stdc++.h> using namespace std;void solve () { int n;cin>>n; vector<int >a (n); for (int i=0 ;i<n;i++) cin>>a[i]; vector<long long >pre (n),suf (n); if (a[0 ]>0 ) pre[0 ]=a[0 ]; for (int i=1 ;i<n;i++) { pre[i]=pre[i-1 ]; if (a[i]>0 ) pre[i]+=a[i]; } if (a[n-1 ]<0 ) suf[n-1 ]=-a[n-1 ]; for (int i=n-2 ;i>=0 ;i--) { suf[i]=suf[i+1 ]; if (a[i]<0 ) suf[i]-=a[i]; } long long ans=0 ; for (int i=0 ;i<n;i++) { ans=max (ans,pre[i]+suf[i]); } cout<<ans<<'\n' ; }int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); int tt;cin>>tt; while (tt--) { solve (); } return 0 ; }
蒋大佬的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <bits/stdc++.h> using namespace std;void solve () { int n;cin>>n; vector<int >a (n); for (int i=0 ;i<n;i++) cin>>a[i]; long long suf=0 ; long long pre=0 ; for (int i=0 ;i<n;i++) { if (a[i]<0 ) { suf+=a[i]; } } long long ans=-suf; for (int i=0 ;i<n;i++) { if (a[i]<0 ) { suf-=a[i]; } else { pre+=a[i]; } ans=max (ans,pre-suf); } cout<<ans<<'\n' ; }int main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); int tt;cin>>tt; while (tt--) { solve (); } return 0 ; }
D 看起来很简单,只需要从后往前尝试遍历即可;忘记异或怎么操作了