20240602比赛总结
T1 等差子序列
https://gxyzoj.com/d/hzoj/p/3638
主要在枚举的方法上,要选小常数较小的方法
30pts:
枚举前两个数,然后算出第三个数的值,看位置是否满足条件,最慢的点会跑到1.16s
100pts:
上面的方法中,不是每组数都可以满足条件,可能会出现大于n或小于1的情况,但是却无法避免对它的的枚举,所以常数较大
可以发现,当第一个数和差确定时,数列必然确定了,所以考虑枚举差,显然,差的范围是固定的,下界为
所以,只需要判断这三个的位置关系即可
代码:
#include<cstdio>
using namespace std;
int T,n,a[10005],t[10005];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
t[a[i]]=i;
}
int fl=0;
for(int i=1;i<=n;i++)
{
for(int j=(1-i)/2;j<=(n-i)/2;j++)
{
int x=t[i],y=t[i+j],z=t[i+j+j];
if(y<z&&x<y)
{
fl=1;
break;
}
}
if(fl) break;
}
if(fl) printf("Y\n");
else printf("N\n");
}
return 0;
}
T2 [Jsoi2015]非诚勿扰
https://gxyzoj.com/d/hzoj/p/450
考场上因为题意的模糊,直接猜了3个结论,加上自己多此一举将括号拆了,导致推了接近1h
首先考虑位于每个位置的男性被选中的概率,假设有k个人,则显然
……
显然可以发现,每一项都是上面一项的
(此处可以用公式直接求解,也可以暴力相加)
因为这些事件的概率和为1,则
接下来考虑如何计算,最简单的方法就是倒序枚举女性,再枚举男性,然后记录有多少的概率有编号比当前女性大,选的男性的比当前编号小,可以用前缀和实现
但是可以发现,m其实并不大,所有有很多的枚举是多余的,考虑优化
其实可以在倒序枚举完女性后,就可以只枚举选择的男性,但是此时就不能用前缀和了,因为要实现单点修改区间查询,所有可以使用树状数组
代码:
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
int n,m,cnt[500005];
double P,p[500005],ans;
vector<int>v[500005];
double sum[500005];
int lowbit(int x)
{
return x & (-x);
}
void add(int x,double val)
{
while(x<=n)
{
sum[x]+=val;
x+=lowbit(x);
}
}
double query(int x)
{
double res=0.0;
while(x)
{
res+=sum[x];
x-=lowbit(x);
}
return res;
}
int main()
{
scanf("%d%d",&n,&m);
scanf("%lf",&P);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
cnt[x]++;
v[x].push_back(y);
}
for(int i=1;i<=n;i++)
{
if(!cnt[i]) continue;
sort(v[i].begin(),v[i].end());
}
for(int i=n;i>0;i--)
{
if(!cnt[i]) continue;
double num=1.0,tmp=1.0;
for(int j=2;j<=cnt[i];j++)
{
tmp=tmp*(1.0-P);
num+=tmp;
}
num=1.0/num;
for(int j=0;j<cnt[i];j++)
{
p[j]=num;
num=num*(1.0-P);
}
for(int j=0;j<cnt[i];j++)
{
ans=ans+p[j]*query(v[i][j]-1);
}
for(int j=0;j<cnt[i];j++)
{
add(v[i][j],p[j]);
}
}
printf("%.2lf",ans);
return 0;
}
T3 [CSP-S 2021] 括号序列
https://gxyzoj.com/d/hzoj/p/3710
题面中说一个合法的子串可以由其他子串拼起来,所有考虑区间dp
显然二维是不够的,需要加一维表示状态
-
表示只由*构成的情况,也是题目中的s -
表示形如(……)的情况,要求左右直接被括号包裹且左右括号匹配 -
表示左边由括号开始,右边由结束的情况,类似于(……)……** -
表示两边均为括号且相邻括号之间可以有*的情况 -
表示左边由开始,右边由括号结尾的情况,类似于……**(……) -
表示左右均为括号的情况,类似于……**(……)……**
接下来考虑转移
-
直接特判 -
可以视作在一个括号里加入一个合法且两边不全为*的串,所以显然是:
可以视作一个由一个括号序列加一个全*的序列组成的
的前面需要括号打头,结尾不用管,但是后面扁蓄是一个完整的括号
可以视作一个由一个以*开头的序列加一个括号序列组成的
则是一个以开头和以结尾的序列拼成的
代码:
#include<cstdio>
#include<algorithm>
#include<string>
#include<iostream>
#define ll long long
using namespace std;
const int p=1e9+7;
int n,m;
string s;
ll dp[505][505][10];
int main()
{
scanf("%d%d",&n,&m);
cin>>s;
s=" "+s;
for(int i=1;i<=n;i++)
{
dp[i][i-1][0]=1;
}
for(int len=1;len<=n;len++)
{
for(int i=1;i<=n-len+1;i++)
{
int j=i+len-1;
if(len<=m&&(s[j]=='?'||s[j]=='*'))
{
dp[i][j][0]=dp[i][j-1][0];
}
if(len>1)
{
if((s[i]=='?'||s[i]=='(')&&(s[j]=='?'||s[j]==')'))
{
dp[i][j][1]=(dp[i+1][j-1][0]+dp[i+1][j-1][2]+dp[i+1][j-1][3]+dp[i+1][j-1][4])%p;
}
for(int k=i;k<=j-1;k++)
{
dp[i][j][2]=(dp[i][j][2]+dp[i][k][3]*dp[k+1][j][0]%p)%p;
dp[i][j][3]=(dp[i][j][3]+(dp[i][k][2]+dp[i][k][3])%p*dp[k+1][j][1]%p)%p;
dp[i][j][4]=(dp[i][j][4]+(dp[i][k][4]+dp[i][k][5])%p*dp[k+1][j][1]%p)%p;
dp[i][j][5]=(dp[i][j][5]+dp[i][k][4]*dp[k+1][j][0])%p;
}
}
dp[i][j][3]=(dp[i][j][3]+dp[i][j][1])%p;
dp[i][j][5]=(dp[i][j][5]+dp[i][j][0])%p;
}
}
printf("%lld",dp[1][n][3]);
return 0;
}
T4 围栏障碍训练场
https://gxyzoj.com/d/hzoj/p/3678
注意读题,起始点所在的栅栏也要走
显然,最简单的方法就是从上往下依次枚举,但是时间复杂度为
显然,从上方的一个点显然只能到达下方最近的一个栅栏,所以考虑逆推
但是如何求该点会到达哪个栅栏,显然这是一个区间覆盖问题,
代码:
#include<cstdio>
#include<algorithm>
#include<cmath>
#define ll long long
#define lid id<<1
#define rid id<<1|1
using namespace std;
int n,s,p=1e5+1,m=2e5+5;
struct node{
int l,r;
}a[50005];
struct seg_tree{
int l,r,val,lazy;
}tr[1000006];
ll dpl[50005],dpr[50005];
void build(int id,int l,int r)
{
tr[id].l=l,tr[id].r=r;
tr[id].lazy=-1;
if(l==r)
{
tr[id].val=0;
tr[id].lazy=-1;
return;
}
int mid=(l+r)>>1;
build(lid,l,mid);
build(rid,mid+1,r);
return;
}
void pushdown(int id)
{
if(tr[id].lazy!=-1)
{
tr[lid].lazy=tr[rid].lazy=tr[id].lazy;
tr[lid].val=tr[rid].val=tr[id].lazy;
tr[id].lazy=-1;
}
}
void update(int id,int l,int r,int c)
{
if(tr[id].l==l&&tr[id].r==r)
{
tr[id].val=tr[id].lazy=c;
return;
}
pushdown(id);
int mid=(tr[id].l+tr[id].r)>>1;
if(r<=mid) update(lid,l,r,c);
else if(l>mid) update(rid,l,r,c);
else update(lid,l,mid,c),update(rid,mid+1,r,c);
}
int query(int id,int x)
{
if(tr[id].l==tr[id].r)
{
return tr[id].val;
}
pushdown(id);
int mid=(tr[id].l+tr[id].r)>>1;
if(x<=mid) return query(lid,x);
else return query(rid,x);
}
int main()
{
scanf("%d%d",&n,&s);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].l,&a[i].r);
a[i].l+=p,a[i].r+=p;
}
build(1,1,m);
a[0].l=a[0].r=p;
for(int i=1;i<=n;i++)
{
int x=query(1,a[i].l),y=query(1,a[i].r);
dpl[i]=min(dpl[x]+abs(a[i].l-a[x].l),dpr[x]+abs(a[i].l-a[x].r));
dpr[i]=min(dpl[y]+abs(a[i].r-a[y].l),dpr[y]+abs(a[i].r-a[y].r));
update(1,a[i].l,a[i].r,i);
}
printf("%lld",min(dpl[n]+abs(a[n].l-s-p),dpr[n]+abs(a[n].r-s-p)));
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律