D. Omkar and Bed Wars
大意:n個人站成一個圈,i的右邊是i+1,n的右邊是1。他們初始有一個朝向(朝左/右)。每次操作可以讓一個人轉向(左變右或相反)。要求不出現連續三個朝左或連續三個朝右的人,求最小操作數
拿到題容易想到貪心。當時我仔細思考了億年,覺得寫起來麻煩得一批,於是投入dp的懷抱
令 \(dp[i][i_1][i_2][k_1][k_2]\) 為前 \(i\) 個人的代價最小值,其中第 \(1\) 個人朝向 \(i_1\),第 \(2\) 個人朝向 \(i_2\),第 \(i-1\) 個人朝向 \(k_1\),第 \(i\) 個人朝向 \(k_2\)。相信大家能一眼看出狀態轉移方程我就懶得講了
復雜度 \(O(16n)\)
#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
#define mst(a,x) memset(a,x,sizeof(a))
#define fi first
#define se second
//#define endl "\n"
mt19937 rnd(chrono::high_resolution_clock::now().time_since_epoch().count());
int cansel_sync=(ios::sync_with_stdio(0),cin.tie(0),0);
const int N=200010; typedef long long ll; const int inf=~0u>>2; const ll INF=~0ull>>2; ll read(){ll x; if(scanf("%lld",&x)==-1)exit(0); return x;} typedef double lf; const lf pi=acos(-1.0); lf readf(){lf x; if(scanf("%lf",&x)==-1)exit(0); return x;} typedef pair<ll,ll> pii; template<typename T> void operator<<(vector<T> &a,T b){a.push_back(b);}
const ll mod=(1?1000000007:998244353); ll mul(ll a,ll b,ll m=mod){return a*b%m;} ll qpow(ll a,ll b,ll m=mod){ll ans=1; for(;b;a=mul(a,a,m),b>>=1)if(b&1)ans=mul(ans,a,m); return ans;}
#define int ll
int a[N]; char s[N];
int dp[N][2][2][2][2];
void Solve(){
int n=read();
scanf("%s",s);
repeat(i,0,n)a[i]=s[i]=='R';
repeat(i,0,n)
repeat(i1,0,2)
repeat(i2,0,2)
repeat(k1,0,2)
repeat(k2,0,2)
dp[i][i1][i2][k1][k2]=inf;
repeat(i1,0,2)
repeat(i2,0,2){
dp[1][i1][i2][i1][i2]=(a[0]!=i1)+(a[1]!=i2);
}
repeat(i,2,n)
repeat(i1,0,2)
repeat(i2,0,2)
repeat(k1,0,2)
repeat(k2,0,2)
repeat(k3,0,2)
if(!(k1==k2 && k2==k3)){
dp[i][i1][i2][k2][k3]=min(dp[i][i1][i2][k2][k3],
dp[i-1][i1][i2][k1][k2]+(a[i]!=k3));
}
ll ans=inf;
repeat(i1,0,2)
repeat(i2,0,2)
repeat(k1,0,2)
repeat(k2,0,2){
if(!(k1==k2 && k1==i1))
if(!(k2==i1 && k2==i2))
ans=min(ans,dp[n-1][i1][i2][k1][k2]);
}
cout<<ans<<endl;
}
signed main(){
//freopen("data.txt","r",stdin);
int T=1; T=read();
while(T--)Solve();
return 0;
}
E. Omkar and Duck
大意:(交互題)先告訴你 \(n\),讓你給出 \(n\times n\) 的矩陣,然后 \(q\) 組詢問,每次會給出 矩陣中 從右上到左下的某一最短路徑上 的數字之和,輸出路徑長什么樣
這題我寫了兩遍題解了,全都不滿意刪掉了,氣死,直接上代碼
#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
#define mst(a,x) memset(a,x,sizeof(a))
#define fi first
#define se second
//#define endl "\n"
mt19937 rnd(chrono::high_resolution_clock::now().time_since_epoch().count());
int cansel_sync=(ios::sync_with_stdio(0),cin.tie(0),0);
const int N=30; typedef long long ll; const ll INF=~0ull>>2; ll read(){ll x; if(scanf("%lld",&x)==-1)exit(0); return x;}
#define int ll
int n,a[N][N]; //a即要輸出的那個矩陣
pair<ll,ll> g[N][N]; //g[i][j]表示如果走到(i,j)這個位置,那么走過的數字之和一定在區間[g[i][j].first,g[i][j].second]內
//g[][]要求在所有左下-右上的斜線上的所有點,其值域兩兩不相同,並且單增
vector<pair<ll,ll>> ans;
void Solve(){
int n=read();
a[0][0]=0;
g[0][0]={0,0};
repeat(sum,1,n*2-1){ //遍歷斜線
int pre=-1;
repeat(j,0,n)
repeat(i,0,n)
if(i+j==sum){ //處理斜線上的點
int mn=INF,mx=-INF;
if(i)mn=min(mn,g[i-1][j].fi),mx=max(mx,g[i-1][j].se);
if(j)mn=min(mn,g[i][j-1].fi),mx=max(mx,g[i][j-1].se); //此時,[mn,mx]為前兩個點的值域的並集
g[i][j]={pre+1,pre+1+mx-mn}; //pre+1是為了與剛剛處理的點值域剛好不相交
a[i][j]=g[i][j].fi-mn;
pre=pre+1+mx-mn; //值域最大值
}
}
repeat(i,0,n){
repeat(j,0,n)
printf("%lld ",a[i][j]);
puts("");
}
fflush(stdout);
int q=read();
while(q--){
ans.clear();
int s=read(); int x=n-1,y=n-1; ans.push_back({x,y});
while(x || y){ //模擬一遍,把路徑模擬出來
if(!x)y--;
else if(!y)x--;
else{
s-=a[x][y];
if(g[x-1][y].fi<=s && g[x-1][y].se>=s)
x--;
else y--;
}
ans.push_back({x,y});
}
repeat_back(i,0,ans.size())
printf("%lld %lld\n",ans[i].fi+1,ans[i].se+1);
fflush(stdout);
}
}
signed main(){
//freopen("data.txt","r",stdin);
int T=1; //T=read();
while(T--)Solve();
return 0;
}
F. Omkar and Landslide
發生了一個恐怖的事情

我2020年1月的時候隨手出了一道題,當時是打算給noip提高組出題,並且鍛煉一下出題能力。結果剛好和這題撞了,甚至輸入輸出格式也是一樣的,唯一區別是我的n是2e5,這題是1e6,還有高度我是1e9,這題是1e12(有區別嗎?)
(當時覺得,從算法角度看,應該是不到noip提高組的難度的,也就寫起來有點繁瑣)
蕪湖,找來以前的代碼直接嘿嘿嘿,老鐵們我做得對嗎
大意:給出n個嚴格單調遞增的數列,不斷重復如下操作直到無法操作:選擇一個i滿足 a[i+1]-a[i]>=2,然后 a[i+1]--; a[i]++;
憋說了,大模擬,莽就完了。先a[i]-=i,然后模擬一個棧,每個元素表示一段相同的a[i],然后就是各種討論,好煩啊,還能咋做捏
我回來了。不是吧阿sir,原來這題是我那題的簡單版本,因為我的題目的初始序列是單調不減的,這題是單調遞增的,那最終狀態只與sum(a[i])有關。那為什么放F題位置啊喂!
#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
#define mst(a,x) memset(a,x,sizeof(a))
#define fi first
#define se second
//#define endl "\n"
mt19937 rnd(chrono::high_resolution_clock::now().time_since_epoch().count());
int cansel_sync=(ios::sync_with_stdio(0),cin.tie(0),0);
const int N=1000010; typedef long long ll; const int inf=~0u>>2; const ll INF=~0ull>>2; ll read(){ll x; if(scanf("%lld",&x)==-1)exit(0); return x;} typedef double lf; const lf pi=acos(-1.0); lf readf(){lf x; if(scanf("%lf",&x)==-1)exit(0); return x;} typedef pair<ll,ll> pii; template<typename T> void operator<<(vector<T> &a,T b){a.push_back(b);}
#define int ll
int n,a[N];
vector<pii> s;
void Solve(){
n=read();
repeat(i,1,n+1)
a[i]=read(),a[i]-=i;
s.push_back({a[1],1});
repeat(i,2,n+1){
if(a[i]<s.back().first){
s.push_back({a[i],i});
continue;
}
if(a[i]==s.back().first)continue;
while(s.size()>1 && a[i]-s.back().first>i-s.back().second){
a[i]-=i-s.back().second;
s.pop_back();
}
if(s.size()==1){
int t=(a[i]-s.back().first)/(i-1+1);
a[i]-=t*(i-1);
s.back().first+=t;
}
if(a[i]==s.back().first)continue;
int d=a[i]-s.back().first;
pii t=s.back();
if(s.size()==1)s.back().first++;
else s.pop_back();
s.push_back({t.first,t.second+d});
}
s.push_back({0,INF});
int p=0;
repeat(i,1,n+1){
if(s[p+1].second==i)p++;
printf("%lld ",s[p].first+i);
}
puts("");
}
signed main(){
//freopen("data.txt","r",stdin);
int T=1; //T=read();
while(T--)Solve();
return 0;
}
