不得不說這場div3是真的出的好,算得上是從我開始打開始最有趣的一場div3。因為自己的號全都藍了,然后就把不經常打比賽的dreagonm的號借來打這場,然后...比賽結束rank11(幫dreagonm上藍果然沒有食言qwq)。

(震驚...HA省A隊CF青名...)
upd:system test之后的最終排名是rank9,dreagonmTQL!!!

CF1157A Reachable Numbers
水題,分析一下不難發現不超過\(10\)次就會少一位,然后\(10^9\)范圍內的數可以到達的數個數顯然不多,不妨大力模擬,然后把出現的數扔進set,最后輸出set.size()即可。(開到\(10^6\)不會T也不會有被卡次數的危險)
#include<bits/stdc++.h>
using namespace std;
int n;
set<int>s;
int main(){
scanf("%d",&n);
for(int i=1;i<=1e6;i++){
s.insert(n);
n+=1;
while(n%10==0){
n/=10;
}
}
printf("%d\n",(int)s.size());
return 0;
}
CF1157B Long Number
注意是選擇一段連續的區間,把區間內的數全部替換為映射值。不難發現可能會增大也有可能減小,為了讓數盡可能變大,考慮采取貪心的策略,找到第一個能使得原數變大的位置\(l\),然后從這個位置向后找,找到第一個會變小的位置\(r\),則變化區間為\([l,r)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n;
char s[N];
int a[15];
char ans[N],t[N];
int fir,lst;
int main(){
scanf("%d",&n);
scanf("%s",s+1);
for(int i=1;i<=9;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
if(s[i]-'0'<a[s[i]-'0']){
fir=i;
break;
}
}
if(!fir){
printf("%s\n",s+1);
return 0;
}
for(lst=fir;lst<=n;lst++){
if(s[lst]-'0'>a[s[lst]-'0']){
lst--;
break;
}
}
if(lst>n){
lst=n;
}
for(int i=fir;i<=lst;i++){
s[i]='0'+a[s[i]-'0'];
}
printf("%s\n",s+1);
return 0;
}
CF1157C Increasing Subsequence
C1
顯然,貪心就行了,因為選完小的還能選大的,答案不會變劣。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n;
int a[N],now,l,r;
char op[N];
int cntop;
int main(){
scanf("%d",&n);
l=1;r=n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
while(l<=r){
if(a[l]>now&&a[r]>now){
if(a[l]<a[r]){
op[++cntop]='L';
now=a[l];
l++;
}
else{
op[++cntop]='R';
now=a[r];
r--;
}
}
else if(a[l]>now){
op[++cntop]='L';
now=a[l];
l++;
}
else if(a[r]>now){
op[++cntop]='R';
now=a[r];
r--;
}
else{
break;
}
}
printf("%d\n%s\n",cntop,op+1);
return 0;
}
C2
和C1不一樣的是,不保證任意兩兩不同。那么考慮和C1不同的點,不難發現在於兩端的數的大小關系多了相等的情況,其他的也沒什么不同。顯然,如果兩端相同,之后就只能從一邊選,那么對於這種情況直接貪心選擇最多的一邊選即可。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n;
int a[N],now,l,r;
char op[N];
int cntop;
int main(){
scanf("%d",&n);
l=1;r=n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
while(l<=r){
if(a[l]>now&&a[r]>now){
if(a[l]==a[r]){
for(int i=l+1,j=r-1;;i++,j--){
if(a[i]<=a[i-1]){
while(a[r]>now){
op[++cntop]='R';
now=a[r];
r--;
}
break;
}
else if(a[j]<=a[j+1]){
while(a[l]>now){
op[++cntop]='L';
now=a[l];
l++;
}
break;
}
}
}
else if(a[l]<a[r]){
op[++cntop]='L';
now=a[l];
l++;
}
else{
op[++cntop]='R';
now=a[r];
r--;
}
}
else if(a[l]>now){
op[++cntop]='L';
now=a[l];
l++;
}
else if(a[r]>now){
op[++cntop]='R';
now=a[r];
r--;
}
else{
break;
}
}
printf("%d\n%s\n",cntop,op+1);
return 0;
}
CF1157D N Problems During K Days
這題挺有趣的,要求構造\(k\)個正整數和為\(n\),且滿足對於\(1\leq i<k\),\(a_i<a_{i+1}\leq2a_i\)。從約束條件入手,分析下界可知,如果還有\(k\)個數需要確定,而第一個數為\(i\),那么這\(k\)個數在和最小的情況下分別是\(i,i+1,\cdots,i+k-1\),由此可得下界。分析上界可知,和最大的情況下分別是\(i,2i,2^2i,\cdots,2^{k-1}i\),和為\((2^k-1)i\),由此可得上界。因為第一個數可以無窮大但不能小於\(1\),所以約束主要是在下界,那么盡可能避免下界不合法即可,所以對於每個位置\(i\),使\(a_i\)盡可能小即可。以樣例為例,n=8 k=3時,如果\(a_1=1\),那么和最大為\(1+2+4=7\),顯然過小。如果\(a_1=2\),和最小為\(2+3+4=9\),又過大了,那么就不合法。n=9 k=4時,和最小為\(1+2+3+4=10\),亦過大。所以總的思路就是在和的上界不小於剩余的值的情況下選擇最小的\(a_i=\max(\lceil\Large\frac{n}{2^{k-i+1}-1}\normalsize\rceil,a_{i-1}+1)\),然后判斷在取得最小可能值的情況下會不會有下界不合法即可(因為取值已經保證上界合法)。
Trick:double在存儲\(2\)的次方是可以得到精確值,用pow(2.0,x)就能得到精確的\(2^x\),就不需要再擔心\(\large2^{10^5}\)過大的問題。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,k;
long long sum(int u){
return 1LL*u*(u+1)/2;
}
double sumb(int u){
return pow(2.0,u)-1;
}
int a[N];
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++){
int rep=max(a[i-1]+1,(int)ceil((double)n/sumb(k+1-i)));
if(sum(rep+k-i)-sum(rep-1)>n){
printf("NO\n");
return 0;
}
a[i]=rep;
n-=rep;
}
printf("YES\n");
for(int i=1;i<=k;i++){
printf("%d%c",a[i]," \n"[i==k]);
}
return 0;
}
CF1157E Minimum Array
題目意思是給出\(a,b\)兩個數組,要求給\(b\)重新排序,使得\(c_i=(a_i+b_i)\bmod{n}\)的字典序最小。顯然字典序是由高位開始比較的,所以顯然可以貪心解決,只需要讓前面的\(c_i\)盡可能小即可。由模運算的性質可知,每次找到不小於\(a_i\bmod{n}\)的相反數的最小數即可,如果不存在就選所有數的最小值。然后整個過程用multiset維護\(b\)數組即可。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n;
int a[N],b[N];
multiset<int>s;
int c[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
s.insert(b[i]);
}
for(int i=1;i<=n;i++){
int rep=(n-a[i])%n;
auto ite=s.lower_bound(rep);
if(ite==s.end()){
ite=s.begin();
}
c[i]=*ite;
s.erase(ite);
}
for(int i=1;i<=n;i++){
printf("%d%c",(a[i]+c[i])%n," \n"[i==n]);
}
return 0;
}
CF1157F Maximum Balanced Circle
題目意思是從給定的\(n\)個數中選盡可能多的數,使得這些數按照某種順序放到環上后,相鄰兩個數的差不超過\(1\)。顯然對於這道題可以先桶排,然后考慮選出的數中最小的數,如果選出的數最小值和最大值分別為\(l,r\),那么一定需要按照\(l,l+1,\cdots,r,r-1,\cdots,l\)排序,否則相鄰相差就會大於\(1\)。那么不難發現,對於\(\forall i\in(l,r),cnt_i>1\)。由此,可以考慮枚舉\(l\),求出最大的\(r\),顯然,對於\(l'\in(l,r]\),如果將選出的數范圍改為\([l',r]\),那么答案一定會比\([l,r]\)劣,所以枚舉的時間復雜度為\(O(\max a_i)\)。然后考慮一下細節,處理\(l=r,l+1=r,l+1<r\)三種情況即可。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,a[N],c[N],sum[N];
int ans;
int mx,l,r;
int query(int u,int v){
return sum[v]-sum[u-1];
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
++c[a[i]];
}
for(int i=1;i<2e5;i++){
if(c[i]&&c[i+1]){
ans=i;
break;
}
}
if(!ans){
for(int i=1;i<=2e5;i++){
if(c[i]>mx){
ans=i;
mx=c[i];
}
}
printf("%d\n",mx);
for(int i=1;i<=mx;i++){
printf("%d%c",ans," \n"[i==mx]);
}
return 0;
}
for(int i=1;i<=2e5;i++){
sum[i]=sum[i-1]+c[i];
}
for(int i=1,j;i<=2e5;){
if(!c[i]||!c[i+1]){
i++;
continue;
}
else if(c[i+1]==1){
if(mx<query(i,i+1)){
mx=query(i,i+1);
l=i;r=i+1;
}
i++;
}
else{
for(j=i+1;j<=n;j++){
if(c[j]>1){
continue;
}
else{
break;
}
}
if(!c[j]){
--j;
}
if(mx<query(i,j)){
mx=query(i,j);
l=i;r=j;
}
i=j;
}
}
printf("%d\n",mx);
for(int i=l;i<=r;i++){
printf("%d ",i);
}
for(int i=r;i>=l;i--){
for(int j=1;j<c[i];j++){
printf("%d ",i);
}
}
return 0;
}
CF1157G Inverse of Rows and Columns
題目要求求出對於給定的\(n\times m\)的\(01\)矩陣,是否有選擇某些行/列進行反轉后使得按照\(a_{11},a_{12},\cdots,a_{1n},a_{21},\cdots,a_{nn}\)順序排列后有序的方案,如果有則輸出任意可行方案。觀察到題目限制條件,易知合法變化后的矩陣應當至多有一行既有\(0\)也有\(1\),如果有則在這一行之前的所有行均為\(0\),之后的所有行均為\(1\),如果不存在既有\(0\)也有\(1\)的行則前一部分行全為\(0\),后一部分全為\(1\)。觀察到數據范圍\(n,m\leq200\),不妨暴力枚舉按題目順序展開后的第一個\(1\)的位置,然后檢驗是否有可行的方案即可。
#include<bits/stdc++.h>
using namespace std;
const int N=220;
int n,m;
int opa[N],opb[N];
int a[N][N],b[N][N];
bool can(int u,int v){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i<u||(i==u&&j<v)){
b[i][j]=0;
}
else{
b[i][j]=1;
}
}
}
opa[1]=a[1][1]^b[1][1];
for(int i=1;i<=m;i++){
opb[i]=a[1][i]^b[1][i]^opa[1];
}
for(int i=2;i<=n;i++){
opa[i]=a[i][1]^b[i][1]^opb[1];
for(int j=2;j<=m;j++){
if(b[i][j]!=(a[i][j]^opa[i]^opb[j])){
return 0;
}
}
}
return 1;
}
void print(){
printf("YES\n");
for(int i=1;i<=n;i++){
putchar('0'+opa[i]);
}
putchar('\n');
for(int i=1;i<=m;i++){
putchar('0'+opb[i]);
}
putchar('\n');
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(can(i,j)){
print();
return 0;
}
}
}
printf("NO\n");
return 0;
}
