怎么感觉 ARC 困难的永远是 B 题[惊恐]
A.AABCDDEFE
题目分析:
完全可以把相等的位置合并在一起,这样就剩下了 \(6\) 个位置,然后就转化为了第 \(N\) 小的六位数是多少,这应该没人不会吧。
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int a[10];
int main(){
int s;scanf("%d",&s);
s--;
for(int i=6; i>=1; i--){
a[i] = s % 10 + (i == 1);
s /= 10;
}
printf("%d%d%d%d%d%d%d%d%d",a[1],a[1],a[2],a[3],a[4],a[4],a[5],a[6],a[5]);
return 0;
}
B.Grid Rotations
题目分析:
可以发现我们的 \(x\) 和 \(y\) 坐标的翻转是互不影响的,所以就可以分开计算。
(然后傻逼的我就没有发现分开后就是区间翻转,然后直接平衡树就好了)
这个时候也没啥办法了,就硬推呗。
假设我们的给定点为 \((a,b)\),当前考虑的点为 \((x,y)\),考虑其 \(x\) 坐标经过翻转后变成了什么。
这个时候显然需要分类讨论了:
这其实就是 \(a-x\) 在模 \(n\) 意义下的值,所以就根据这个把多次操作合并一下就好了。
具体来说就是不管 \(x\) 只记录多次操作会让前面的 \(a\) 变成什么,然后根据奇偶性判断一下 \(x\) 取正还是负就好了。
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+5;
int posx[N],posy[N],x[N],y[N];
vector<char> v[N];
char s[N];
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int n,m;scanf("%d%d",&n,&m);
for(int i=0; i<n; i++){
scanf("%s",s+1);
for(int j=1; j<=m; j++) v[i].push_back(s[j]);
}
int q,dx = 0,dy = 0;
scanf("%d",&q);
for(int i=1; i<=q; i++){
scanf("%d%d",&x[i],&y[i]);x[i]--;y[i]--;
}
for(int i=q; i>=1; i--){
dx = x[i] - dx;
dy = y[i] - dy;
}
if(q & 1){
for(int i=0; i<n; i++) posx[i] = ((dx - i)%n + n)%n;
for(int i=0; i<m; i++) posy[i] = ((dy - i)%m + m)%m;
}
else{
for(int i=0; i<n; i++) posx[i] = ((dx + i)%n + n)%n;
for(int i=0; i<m; i++) posy[i] = ((dy + i)%m + m)%m;
}
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
printf("%c",v[posx[i]][posy[j]]);
}
printf("\n");
}
return 0;
}
C.Increasing Sequence
题目分析:
看到要求严格单调递增,直觉肯定是先构造出一个 \([0,n-1]\)。
然后其实发现这个条件完全可以去调整,比如说对于一段区间,其 \(1\) 的个数为 \(x\) 而 \(-1\) 的个数为 \(y\),那么将区间全部加 \(1\) 造成的贡献就是 \(x-y\)。
为了使得严格单调递增的条件成立,那么就是要前缀减或者后缀加,就很简单了。
我们最好是选择一个 \(1\) 和 \(-1\) 的个数相差为 \(1\) 的区间,因为这样区间加的值很好计算。
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5+5;
int a[N],x[N],ans;
bool flag= false;
signed main(){
int n;scanf("%lld",&n);
for(int i=1; i<=n; i++) scanf("%lld",&a[i]);
for(int i=1; i<=n; i++) x[i] = i-1,ans += x[i] * a[i];
//后缀加---------------------------------------------------------
int res = 0,pos = n+1;
if(ans > 0){
for(int i=n; i>=1; i--){
res+=a[i];
if(res < 0) { //草,压行真压出事了
pos = i;break;
}
}
if(pos != n + 1) flag = true;
for(int i=pos; i<=n; i++) x[i] += ans;
}
else if(ans < 0){
for(int i=n; i>=1; i--){
res += a[i];
if(res > 0){
pos = i;break;
}
}
if(pos != n + 1) flag = true;
for(int i=pos; i<=n; i++) x[i] += -ans;
}
else flag = true;
//前缀减---------------------------------------------------------
if(!flag){ //前缀减
pos = n + 1,res = 0;
if(ans > 0){
for(int i=1; i<=n; i++){
res+=a[i];
if(res > 0) { //草,压行真压出事了
pos = i;break;
}
}
if(pos != n + 1) flag = true;
for(int i=1; i<=pos; i++) x[i] -= ans;
}
else if(ans < 0){
for(int i=1; i<=n; i++){
res += a[i];
if(res < 0){
pos = i;break;
}
}
if(pos != n + 1) flag = true;
for(int i=1; i<=pos; i++) x[i] -= -ans;
}
else flag = true;
}
if(flag){
printf("Yes\n");
for(int i=1; i<=n; i++) printf("%lld ",x[i]);
}
else printf("No\n");
return 0;
}
D.Sum of Sum of Digits
题目分析:
因为低位会对高位产生影响,所以我们显然需要从低到高考虑,这个题显然贪心不大对,所以就考虑 \(dp\)。
知道了上面这一条其实就很容易可以想到这样的一个 \(dp\) 状态:\(dp[i][j][0/1]\) 表示前 \(i\) 位,考虑了前 \(j\) 个数,其中第 \(j\) 个数当前位没有/有进位的最小的 \(f\) 的和。
推推转移的话会发现,仿佛根本推不出来啊,而且统计答案也显然很难做,那么就考虑一下是不是状态有啥问题。
第一维没啥说的了,看第二维和第三维,这个其实本质上是因为我们的转移肯定需要知道各个数的进位情况,但是我们并不需要以这种形式放到状态里啊,因为对于当前位会产生进位贡献的一定是按当前位从大到小排序后的一个前缀,所以其实直接记 \(dp[i][j]\) 表示前 \(i\) 位当前有 \(j\) 个进位的最小的 \(f\) 的和。
这样就很简单了,就是枚举一下这一位选什么,然后计算一下进位与进位的贡献就好了。如果是由于上一位的进位而产生的进位其实就是会对我们这一位的和产生一个 \(-9\) 的贡献。
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5+5;
const int INF = 1e9+5;
int res,a[N],pos[N],dp[11][N];
bool cmp(int x,int y){
return a[x] % res > a[y] % res;
}
signed main(){
int n;scanf("%lld",&n);
for(int i=1; i<=n; i++) scanf("%lld",&a[i]),pos[i] = i;
memset(dp,0x3f,sizeof(dp));
res = 1;dp[0][0] = 0;
for(int i=1; i<=10; i++,res *= 10){
sort(pos+1,pos+n+1,cmp);
for(int j=0; j<=9; j++){
int cnt = 0,sum = 0;
for(int k=1; k<=n; k++) cnt += ((a[k] / res)%10 + j) > 9,sum += ((a[k] / res)%10 + j)%10;
dp[i][cnt] = min(dp[i][cnt],dp[i-1][0] + sum);
for(int k=1; k<=n; k++){
if((a[pos[k]] / res)%10 + j == 9) cnt++,sum -= 9;
else sum++;
dp[i][cnt] = min(dp[i][cnt],dp[i-1][k] + sum);
}
}
}
int ans = INF;
for(int i=0; i<=n; i++) ans = min(ans,dp[10][i] + i);
printf("%lld\n",ans);
return 0;
}