csp-s总结
考前
考前考了几场noip和一场csp模拟赛,也考得的还行吧,虽然每一次基本上都有一点挂分,自我感觉还行,所以考前就写了一些模板,但最后一个也没用上。
考中
先通读了一遍题,发现第一题简单,二三题貌似可做,第四题貌似不可做。
所以决定先写快速写完第一题。第一题确实很简单,一眼就是贪心,然后自己搞了个双指针的做法,最开始的时候写的有点复杂,给自己绕进去了,所以10分钟没调出来就直接换了写法,重构代码,半个小时的样子调完了第一题,又验了一些数据,确认无误后开始写T2。
先是不管第二个答案,直接模拟第一个问题,根据加速度分类即可,然后我们发现检测到超速的是一段区间,所以问题转化为用尽量少的点覆盖给定的区间,一开始想的是贪心,但是自己把自己hack了,对于被完全覆盖的区间直接把大区间踢掉即可直接贪心即可,但我以为贪心不可做就想写了用前缀后求差分约束又害怕spfa被卡不敢写,当我决定写差分约束是还有2个小时,但是我写第二题的模拟花费了很长的时间写完后剩1个半小时,差分约束写完后一直过不去给的大样例,当时心态就不好了。我就先放下第二题,去写三四题的暴力,第三题觉得像dp,随便推了下式子发现不太好做,所以写了一个非常简单的暴力,第四题急匆匆看了一眼,连暴力都不会,就又滚回去调第二题,改对了一些东西,但还是过不了样例,又在手模样例,结果模拟到最后发现可能是模拟出了问题,区间不对所以又重新调整直到最后一秒。
赛后大家人均300,说第三题dp直接做,但我根本就没怎么思考第三题,失去了做出第三题的机会。所以如何在考试中分配时间还要修炼。
考后反思
1.代码实现能力太差了,模拟耗费的时间太长了还容易错,多写一点模拟题。
2.调试,用动态调试太浪费时间了,要改掉这个习惯,尽量用静态调试。
3.不要随便下笔,在纸上写好细节注意事项后再动手。
4.一定要保持敬畏之心,不能因为模拟赛放松警惕。
考后改题
T1
100->100,是对的。
T2
0—>0 想法是对的,最后发现是输出答案的时候把\(m\)写成\(n\),改回 \(m\) 后可以过 \(6\) 个点(剩下 \(4\) 个点应写的是spfa所以被卡了,用dij应该能过),还有一个是因为有一种特殊情况即一开始等于限制的速度,加速度为正的情况用的是\(lower_bound\).重新写一个贪心就过了。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const long double inf=1e7,eps=1e-8;
int t,n,m,V,L,cnt,dep[N],tot,nxt[2*N],hd[N],go[2*N],jz[2*N],las;
struct node
{
int d,v,a,stid,edid;
double st,ed;
}ca[N];
struct stu
{
int l,r;
}qj[N];
long double p[N];
bool cmp(stu a,stu b){
return a.r != b.r ? a.r < b.r : a.l < b.l;
}
int main()
{
freopen("detect.in","r",stdin);
freopen("detect.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>t;
while(t--)
{
memset(hd,0,sizeof(hd));
tot=cnt=las=0;
cin>>n>>m>>L>>V;
V=V*V;
int ans1=0;
for(int i=1;i<=n;i++)
{
cin>>ca[i].d>>ca[i].v>>ca[i].a;
ca[i].st=ca[i].ed=inf;
int v=ca[i].v*ca[i].v;
long double x;
if(ca[i].a==0)
{
if(v>V)ca[i].st=ca[i].d;
}
if(ca[i].a>0)
{
x=(1.0*V-1.0*v)/(1.0*2*ca[i].a);
if(x<0) ca[i].st=ca[i].d;
else ca[i].st=ca[i].d+x;
}
if(ca[i].a<0)
{
x=(1.0*V-1.0*v)/(1.0*2*ca[i].a);
if(x>=0)ca[i].st=ca[i].d,ca[i].ed=ca[i].d+x;
}
}
for(int i=1;i<=m;i++)
cin>>p[i];
for(int i=1;i<=n;i++)
{
if(ca[i].a==0)
{
if(ca[i].st!=inf)
{
ca[i].stid=lower_bound(p+1,p+m+1,ca[i].st)-p;
ca[i].edid=m;
}
else ca[i].stid=m+1,ca[i].edid=m;
}
if(ca[i].a<0)
{
if(ca[i].st!=inf)
{
ca[i].stid=lower_bound(p+1,p+m+1,ca[i].st)-p;
ca[i].edid=lower_bound(p+1,p+m+1,ca[i].ed)-p-1;
}
else ca[i].stid=m+1,ca[i].edid=m;
}
if(ca[i].a>0)
{
if(ca[i].st==ca[i].d)
{
if(ca[i].v*ca[i].v>V)ca[i].stid=lower_bound(p+1,p+m+1,ca[i].st)-p;
else ca[i].stid=upper_bound(p+1,p+m+1,ca[i].st)-p;
ca[i].edid=m;
}
else
{
ca[i].stid=upper_bound(p+1,p+m+1,ca[i].st)-p;
ca[i].edid=m;
}
}
if(ca[i].edid-ca[i].stid>=0)
{
ans1++;qj[ans1].l=ca[i].stid,qj[ans1].r=ca[i].edid;
}
}
sort(qj+1,qj+ans1+1,cmp);
for(int i=1;i<=ans1;i++)
{
if(qj[i].l<=las)continue;
las=qj[i].r;
cnt++;
}
cout<<ans1<<" "<<m-cnt<<'\n';
}
return 0;
}
T3
是一个比较简单的dp,设 \(dp[i]\) 表示前 \(i\) 个的最大值。很显然就是从前面最近的相同数转移(如果不是将中间的相同数染成相同元素一定更优)所以贡献从三个方面考虑,一是从 \(1\) 到 \(pre[i]+1\) 的最大值即 \(dp[pre[i]+1]\),然后是从 \(pre[i]+1\) 到 \(i-1\) 染成同一元素的贡献(用前缀和维护即可 \(O(1)\) 转移),最后就匹配上后 \(a[i]\) 的贡献,所以就有一个简单好写的 \(O(n)\) dp。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+10,M=1e6+10;
int t,n,las[M],a[N],pre[N];
ll sum[N],dp[N];
void solve()
{
freopen("color.in","r",stdin);
freopen("color.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
pre[i]=las[a[i]];
las[a[i]]=i;
sum[i]=sum[i-1]+((a[i]==a[i-1])?a[i]:0);//全部染成一个颜色
}
for(int i=1;i<=n;i++)
{
if(pre[i]!=0)
{
if(pre[i]==i-1)dp[i]=dp[i-1]+a[i];
else dp[i]=max(dp[i-1],dp[pre[i]+1]+sum[i-1]-sum[pre[i]+1]+a[i]);
}
else dp[i]=dp[i-1];
}
cout<<dp[n]<<'\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>t;
while(t--)
{
solve();
for(int i=1;i<=n;i++)las[a[i]]=0,dp[i]=0;
}
return 0;
}