2022.4.10普及组模拟
A 排座位
没有任何难度的大水题
想复杂了。如果只能是两个相邻的位置交换自然要求逆序对,但是这个题可以随便换,所以我们乱搞。最终的目标就是把 换到第一个位置, 换到第二个位置,以此类推
如果 就不操作,如果 且 则交换。很显然的做法,但是乍一看貌似无法保证交换次数最小,所以有另一个很严谨的做法
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000000+5;
inline int read(){
int x=0;char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^'0');c=getchar();
}
return x;
}
int data[maxn],pos[maxn];
int n;
int main(){
freopen("seat.in","r",stdin);
freopen("seat.out","w",stdout);
n=read();
for(int i=1;i<=n;++i){
data[i]=read();
pos[data[i]]=i;
}
int ans=0;
for(int i=1;i<=n;++i){
if(data[i]!=i){
pos[data[i]]=pos[i];
swap(data[i],data[pos[i]]);
ans++;
}
}
printf("%d\n",ans);
return 0;
}
B 梦中的学校(原题金字塔,《算法竞赛进阶指南》区间DP部分)
对于一个字符串 可能会有多种情况,所以考虑区间DP。定义状态 为 的情况数。
我们只考虑其构成一颗树的情况,所以 的情况是不合法,dfs肯定要从根进去再从根出来。
如果 很显然方案数为1
为了不重复计数,只考虑该范围中第一颗子树的范围,枚举断点 , 被分为 和 两部分, 不相同,第一颗子树大小也就不相同,就不可能产生重复的结构,则有方程
=
状态之间遵循加法原理而子状态之间遵循乘法原理
Code
int solve(int l,int r){
if(l>r)return 0;
if(S[l]!=S[r])return f[l][r]=0;
if(l==r)return f[l][r]=1;
if(f[l][r]!=-1)return f[l][r];
f[l][r]=0;
for(int k=l+2;k<=r;++k){
f[l][r]=(f[l][r]+1ll*solve(l+1,k-1)*solve(k,r))%mod;
}
return f[l][r];
}
printf("%d\n",solve(1,strlen(S+1)));
C 激流突进(SCOI2011糖果)
显然是一个差分约束系统
如果,则
如果,则
如果,则
如果,则
如果,则
如果A>B>C,则<B,A>=<C,B>=<C,A>=1,为了使结果正确,建完图之后跑最长路。
建完图之后很有可能不完全,设一个解,由于其他的解都是正解,很显然有,都有,即,0向所有的点连一条权值为0的边即可
不成立的情况:
如果A>B,B>C,C>A,显然不成立,而且建图后出现环,可以用topo排序处理。最长路算法常用spfa,且spfa跑最长路可以判正环,所以用spfa处理
如果A>A 或 A<A ,显然不成立
这样处理完之后貌似就没什么问题,但是除了正环以外还有另外一种环,A=B=C,边权都为0,所以spfa扫它没啥意义,所以连完1,3,5三种情况后Tarjan缩点,再连2,4情况的边
struct Graph{
struct edge{
int to,next,dis;
}e[maxn*5];
int head[maxn],len;
void Insert(int x,int y,int dis){
e[++len].to=y;e[len].dis=dis;e[len].next=head[x],head[x]=len;
}
bool vis[maxn];
long long cnt[maxn],d[maxn];
bool spfa(int u){
memset(d,0xf3,sizeof(d));
queue<int>q;
q.push(u);
d[u]=1;
vis[u]=1;
cnt[u]=1;
while(!q.empty()){
int x=q.front();q.pop();vis[x]=0;
for(int i=head[x];i;i=e[i].next){
int v=e[i].to;
if(d[v]<d[x]+e[i].dis){
d[v]=d[x]+e[i].dis;
if(!vis[v]){
q.push(v);
cnt[v]++;
vis[v]=1;
if(cnt[v]>n){
return 1;
}
}
}
}
}
return 0;
}
}G;
void work(){
n=read(),m=read();
bool f=0;
for(int i=1;i<=m;++i){
int op=read(),x=read(),y=read();
if(op==1)G.Insert(x,y,0),G.Insert(y,x,0);
if(op==2){if(x==y)f=1;G.Insert(x,y,1);}
if(op==3)G.Insert(y,x,0);
if(op==4){if(x==y)f=1;G.Insert(y,x,1);}
if(op==5)G.Insert(x,y,0);
}
if(f){
printf("-1\n");return;
}
for(int i=n;i>0;--i)G.Insert(0,i,0);//玄学优化
f=G.spfa(0);
if(f){
printf("-1\n");return;
}
long long ans=0;
for(int i=1;i<=n;++i){
ans+=G.d[i];
}
printf("%lld\n",ans);
}
int main(){
work();
return 0;
}
D 奖学金
先按照成绩排序,枚举每一个数作为中位数,答案为+前n/2最小值之和+后n/2最小值之和。最小值之和用优先队列维护即可
赛时十分钟打了个线段树,每次枚举重新建树,暴力求最值 。
Code
int data[maxn],pos[maxn];
struct Pig{
long long cs,mo;
}pig[maxn];
bool cmp(Pig x,Pig y){
return x.cs<y.cs;
}
int n,c,F;
int f[maxn],g[maxn];//f[i] i为中位数前n/2最小值的和, g[i] i为中位数后n/2最小值的和
void work(){
priority_queue<long long>q;
long long sum=0;
for(int i=1;i<=n/2;++i){
q.push(pig[i].mo);sum+=pig[i].mo;
}
for(int i=n/2+1;i<=c-n/2;++i){
f[i]=sum;
if(pig[i].mo<q.top()){sum-=q.top();q.pop();q.push(pig[i].mo);sum+=pig[i].mo;}
}
q=priority_queue<long long>();sum=0;
for(int i=c;i>=c-n/2+1;--i){
q.push(pig[i].mo);sum+=pig[i].mo;
}
for(int i=c-n/2;i>=n/2+1;--i){
g[i]=sum;
if(pig[i].mo<q.top()){sum-=q.top();q.pop();q.push(pig[i].mo);sum+=pig[i].mo;}
}
}
int main(){
freopen("money.in","r",stdin);
freopen("money.out","w",stdout);
n=read(),c=read(),F=read();
for(int i=1;i<=c;++i){
pig[i].cs=read(),pig[i].mo=read();
}
sort(pig+1,pig+c+1,cmp);
long long ans=-1;
work();
for(int i=c-n/2;i>=n/2+1;--i){
if(f[i]+g[i]+pig[i].mo<=F){
ans=pig[i].cs;break;
}
}
printf("%lld\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了