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;
}