SMU2025寒假训练周报3
一.2025牛客寒假算法基础集训营6
1.好伙计猜拳
有点类似最长上升子序列,但是多了个交换操作,只需要用二维数组来表示一下状态就好,转移时假设当前位置为 i,枚举上一个位置 j,如果能把 (或bi,ai)接到 (或bj,aj)后面,则可以花费(i−j−1)*c1来删除两者中间的那段。
查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[1000000],b[1000000];
signed main()
{
//ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t;
cin>>t;
while(t--)
{
int n,c1,c2;
cin>>n>>c1>>c2;
for(int i=1;i<=n;i++)cin>>a[i]>>b[i];
vector<vector<int>>dp(n+10,vector<int>(2,1e18));
dp[0][0]=0;
dp[0][1]=0;
a[n+1]=LLONG_MAX,b[n+1]=LLONG_MAX;
for(int i=1;i<=n+1;i++)
{
for(int j=0;j<i;j++)
{
if(a[i]>=a[j]&&b[i]>=b[j])
{
dp[i][0]=min(dp[i][0],dp[j][0]+(i-j-1)*c1);
dp[i][1]=min(dp[i][1],dp[j][1]+(i-j-1)*c1+c2);
}
if(a[i]>=b[j]&&b[i]>=a[j])
{
dp[i][0]=min(dp[i][0],dp[j][1]+(i-j-1)*c1);
dp[i][1]=min(dp[i][1],dp[j][0]+(i-j-1)*c1+c2);
}
}
}
cout<<min(dp[n+1][0],dp[n+1][1])<<endl;
}
return 0;
}
2.小鸡的排列构造
原题链接:H-小鸡的排列构造_2025牛客寒假算法基础集训营6
打表可以观察出规律,r-l是奇数时倒序就没问题,但是是偶数时我们可以两个两个的排,将i-1排在i前面就能满足所有情况
查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t;
cin>>t;
while(t--)
{
int n,m;
cin>>n>>m;
int ans=0;
for(int i=1;i<=m;i++)
{
int a,b,c;
cin>>a>>b>>c;
ans=b-a;
}
if(ans%2==0)
{
for(int i=n;i>=1;i-=2)
{
if(i!=1)
cout<<i-1<<" "<<i<<" ";
else cout<<1;
}
}
else
for(int i=n;i>=1;i--)
cout<<i<<" ";
cout<<endl;
}
return 0;
}
3.小鸡的排列构造的checker
原题链接:I-小鸡的排列构造的checker_2025牛客寒假算法基础集训营6
主席树板子,找出区间中比目标位置数字大的数的个数,再用边界计算位置即可,注意控制空间
查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxx=3e5+10;
struct node{
int l;
int r;
int sum;
}p[maxx*10];
int a[maxx],b[maxx],root[maxx];
int n,m,tot;
/*--------------主席树--------------*/
int build(int l,int r)
{
int cur=++tot;
p[cur].sum=0;
if(l==r) return cur;
int mid=l+r>>1;
p[cur].l=build(l,mid);
p[cur].r=build(mid+1,r);
return cur;
}
int update(int rot,int pos,int l,int r)
{
int cur=++tot;
p[cur]=p[rot];
p[cur].sum++;
if(l==r) return cur;
int mid=l+r>>1;
if(pos<=mid) p[cur].l=update(p[rot].l,pos,l,mid);
else p[cur].r=update(p[rot].r,pos,mid+1,r);
return cur;
}
int query(int lrot,int rrot,int l,int r,int pos)
{
if(l==r) return p[rrot].sum-p[lrot].sum;
int mid=l+r>>1;
int sum=0;
if(pos<=mid) sum=query(p[lrot].l,p[rrot].l,l,mid,pos)+p[p[rrot].r].sum-p[p[lrot].r].sum;
else sum=query(p[lrot].r,p[rrot].r,mid+1,r,pos);
return sum;
}
int main()
{
int l,r,x;
int t;
scanf("%d",&t);
while(t--)
{
tot=0;
scanf("%d%d",&n,&m);
tot=0;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+1+n);
int len=unique(b+1,b+1+n)-b-1;//离散化
root[0]=build(1,len);
for(int i=1;i<=n;i++) root[i]=update(root[i-1],lower_bound(b+1,b+1+len,a[i])-b,1,n);
while(m--)
{
scanf("%d%d%d",&l,&r,&x);
int zz=query(root[l-1],root[r],1,n,lower_bound(b+1,b+1+len,a[x])-b);
printf("%d\n",r-zz+1);
}
}
return 0;
}
二.个人训练赛4
1.C. Li Hua and Chess
交互题
选定(1,1),这样目标点的范围就缩小到如下位置:
然后对(1,k+1),(k+1,1)提问,可以进一步缩小答案范围 ,对(1,k+1)询问,如果离(1,1)的距离大于(1,k+1)的距离,那么就在右边界,否则就在下边界
查看代码
int query(int x,int y){
if(x>n||y>m) return Inf;
int ans;
cout<<"?"<<" "<<x<<" "<<y<<'\n';
cin>>ans;
return ans;
}
void output(int x,int y){
cout<<"!"<<" "<<x<<" "<<y<<'\n';
}
void solve(){
cin>>n>>m;
int x=query(1,1);
int y=query(1,x+1);
int z=query(x+1,1);
if(y<x) output(y+1,x+1);
else output(x+1,z+1);
}
三.个人练习赛5
1.C. Sequence Master
打表找规律,全0是通用解。当n=1时,答案是a1-a2的绝对值;n=2时,有两种其他合法的(2,2,2,2)和(2,-1,-1,-1);n为大于等于3的奇数时,只能全0;n为大于2的偶数时,还存在(n,-1,-1....,-1)
查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[1000000];
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
int ans=0;
for(int i=1;i<=2*n;i++)cin>>a[i],ans+=abs(a[i]);
if(n==1)
{
cout<<abs(a[1]-a[2])<<endl;
continue;
}
if(n==2)
{
int sum=0;
for(int i=1;i<=4;i++)
{
sum+=abs(a[i]-2);
}
ans=min(ans,sum);
int tmp=0;
for(int i=1;i<=4;i++)
{
tmp=abs(a[i]-2);
for(int j=1;j<=4;j++)
{
if(i==j)continue;
tmp+=abs(a[j]+1);
}
ans=min(ans,tmp);
}
cout<<ans<<endl;
continue;
}
if(n&1)cout<<ans<<endl;
else
{
int sum=0;
for(int i=1;i<=2*n;i++)sum+=abs(a[i]+1);
for(int i=1;i<=2*n;i++)
{
ans=min(ans,sum-abs(a[i]+1)+abs(a[i]-n));
}
cout<<ans<<endl;
}
}
return 0;
}