codeforces round 981 A~F 题解
A
题意
两个玩家正在进行游戏,第 $ i $ 轮游戏可以让最初在原点的棋子向左或向右移动 $ 2i-1 $ 格(先手向左移动,后手向右移动)。问当棋子的坐标的绝对值大于 $ N $ 的情况是谁的回合。
题解
由于 $ N $ 的范围并不大,直接模拟过程就行。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
void solve()
{
int n;
cin>>n;
int x=0;
for(int i=1;i<=1000;i++)
{
if(i%2==1) x-=i*2-1;
else x+=i*2-1;
if(x>n||x<-1*n)
{
if(i%2==1) cout<<"Sakurako"<<endl;
else cout<<"Kosuke"<<endl;
return ;
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
B
题意
在 $ N × N $ 的方阵中每次可以选择任意大小的方阵,让正对角线上的所有元素都加上 $ 1 $ 。问需要进行多少次操作能让这个方阵中所有元素都大于等于 $ 0 $ 。
题解
每次都选一整条斜线,让上面的元素都加 $ 1 $ 即可。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int maxn=505;
int a[maxn][maxn];
void solve()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>a[i][j];
int ans=0;
for(int j=1;j<=n;j++)
{
int minnum=1e15;
for(int k=0;j+k<=n&&1+k<=n;k++)
{
minnum=min(minnum,a[1+k][j+k]);
}
if(minnum>=0) minnum=0;
else ans+=abs(minnum);
}
for(int i=2;i<=n;i++)
{
int minnum=1e15;
for(int k=0;k+1<=n&&i+k<=n;k++)
{
minnum=min(minnum,a[i+k][1+k]);
}
if(minnum>=0) minnum=0;
else ans+=abs(minnum);
}
cout<<ans<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
C
题意
一个长度为 $ N $ 的序列 $ a $ ,你可以无数次交换 $ a_i $ 和 $ a_{N-i+1} $ ,问可以达到的 $ a_i = a_{i+1} $ 最少数量为多少。
题解
神秘贪心,考虑交换是否比不交换更优即可,有点类似于排序之后跑 $ 01 $ 背包的那种题型。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int maxn=2e5+10;
int a[maxn],n;
int dp[maxn][2];
void solve()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n/2;i++)
{
if((a[i]==a[i-1])+(a[n+1-i]==a[n+2-i])>=(a[i]==a[n-i+2])+(a[n-i+1]==a[i-1]))
swap(a[i],a[n-i+1]);
}
int ans=0;
for(int i=1;i<=n-1;i++) ans+=(a[i]==a[i+1]);
cout<<ans<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
D
题意
在长度为 $ N $ 的序列 $ a $ 上尽可能多的划分出这样的的不重叠子区间 [$ L $ , $ R $] 使得 $ a_L + ... + a_R = 0 $ 。
题解
很典的前缀和处理后进行动态规划,$ dp_i $ 表示处理完前 $ i $个数能划分出的不重叠子序列。很显然写出状态转移:
$ dp_i = dp_j +1 $ , $ s_i = s_j $
$ dp_i = dp_{i-1} $ , else
其中 $ s $ 表示前缀和,这个用 $ map $ 存一下即可。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int maxn=2e5+10;
int a[maxn],s[maxn],n,dp[maxn];
map<int,int>ds,vis;
void solve()
{
vis.clear();
ds.clear();
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=s[i-1]+a[i];
}
vis[0]=1;
for(int i=1;i<=n;i++)
{
dp[i]=dp[i-1];
if(vis[s[i]])
{
dp[i]=max(dp[i],dp[ds[s[i]]]+1);
ds[s[i]]=i;
}
else
{
vis[s[i]]=1;
ds[s[i]]=i;
}
}
cout<<dp[n]<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
E
题意
对于一个长度为 $ N $ 的排列 $ p $ ,用尽可能少的如下操作:交换 $ p_i $ 与 $ p_j $。
使得整个排列中任意的 $ i $ 满足如下两个条件之一:
- $ p_i = i$
- $ p_{p_i} = i$
题解
首先根据排列关系可以建图,那么建图后会发现,整张图会形成若干个自环或其他环。那么根据上面两个条件可以发现,最后的终态是让所有的点都形成自环或者是二元环。那么交换操作就类似于断两条边,然后重连两条边。显然最优策略就是重连的两条边后形成二元环,自环无视掉即可。所以遍历整张图,找到每个环的大小 $ siz $ ,它对答案的贡献就是 $ (siz - 1)/2 $ 。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int maxn=1e6+10;
int p[maxn],n;
bool vis[maxn];
vector<int>ds[maxn];
int ans=0;
void dfs(int u,int sum)
{
if(vis[u])
{
ans+=(sum-1)/2;
return ;
}
vis[u]=1;
for(auto v:ds[u]) dfs(v,sum+1);
}
void solve()
{
int n;
cin>>n;
ans=0;
for(int i=1;i<=n;i++) vis[i]=0;
for(int i=1;i<=n;i++) ds[i].clear();
for(int i=1;i<=n;i++)
{
cin>>p[i];
ds[i].push_back(p[i]);
}
for(int i=1;i<=n;i++)
{
dfs(i,0);
}
cout<<ans<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
F
题意
找到第 $ n $ 个能被 $ k $ 整除的斐波拉契项。
题解
打表题,首先先暴力找到第一个能被 $ k $ 整除的斐波拉契项,然后通过打表可以发现:在取 $ k $ 模数的斐波拉契数列的情况下,整个数列会出现循环情况,那么直接找到第一个能被 $ k $ 整除的项然后乘 $ N $ 即可。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int mod=1e9+7;
const int maxn=2e6+10;
int dp[maxn];
void solve()
{
int n,k;
cin>>n>>k;
dp[1]=1%k;
dp[2]=1%k;
n=n%mod;
if(dp[1]==0)
{
cout<<n<<endl;
return ;
}
int i=3;
while(1)
{
dp[i]=(dp[i-1]+dp[i-2])%k;
if(dp[i]==0)
{
cout<<(n*i)%mod<<endl;
return ;
}
i++;
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int t=1;
cin>>t;
while(t--)
{
solve();
}
return 0;
}