NOIP2018普及组解题报告

99 篇文章 1 订阅
76 篇文章 0 订阅

凉凉记

洛谷 5015 标题统计

代码(题目过水)

#include <cstdio>
#include <cctype>
using namespace std;
int ans; char c;
int main(){
    while ((c=getchar())!=EOF) ans+=isalnum(c)>0;
    return !printf("%d",ans);
}

洛谷 5016 龙虎斗

题目链接

分析

有许多蒟蒻都漏掉了m号的情况,结果可能就与一等奖失之交臂,思路就是对于每一个节点判断最小值。(我朋友竟然打了二分,与一等奖失之交臂,当然,这是后话)


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
typedef long long bnt;
const int N=100010;
int n,m,choi,a[N+1],ans; bnt ans1,ans2,sum;
inline int iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
    return ans;
}
int main(){
    n=iut(); for (rr int i=1;i<=n;++i) a[i]=iut();
    m=iut(); rr int x=iut(); a[x]+=iut(); choi=iut();
    for (rr bnt i=1;i<m;++i) ans1+=(m-i)*a[i];
    for (rr bnt i=m+1;i<=n;++i) ans2+=(i-m)*a[i];
    ans=m; sum=ans1<ans2?ans2-ans1:ans1-ans2;//m号初始化
    for (rr bnt i=1;i<m;++i){//轩轩
        ans1+=(m-i)*choi;
        rr bnt t=ans1<ans2?ans2-ans1:ans1-ans2;
        if (t<sum) sum=t,ans=i;
        ans1-=(m-i)*choi;
    }
    for (rr bnt i=m+1;i<=n;++i){//凯凯
        ans2+=(i-m)*choi;
        rr bnt t=ans1<ans2?ans2-ans1:ans1-ans2;
        if (t<sum) sum=t,ans=i;
        ans2-=(i-m)*choi;
    }
    printf("%d",ans);
    return 0;
}

洛谷 5017 摆渡车

题目链接

分析(线性dp)

作为这套题最难的一道题,我差点悲剧,不过这道题是一道dp的题目,设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示第 i i i个同学(排序后)等车 j j j分钟后的最短等待时间,那么在同一段的时候 d p i , j = d p i − 1 , t i − t i − 1 + j + j dp_{i,j}=dp_{i-1,t_i-t_{i-1}+j}+j dpi,j=dpi1,titi1+j+j
要新开一段的时候 d p i , j = m i n t i − 1 + k + m ≤ t i + j { d p i − 1 , k } + j dp_{i,j}=min_{t_{i-1}+k+m\leq t_i+j}\{dp_{i-1,k}\}+j dpi,j=minti1+k+mti+j{dpi1,k}+j
直到现在,时间复杂度已经是 O ( n m 2 ) O(nm^2) O(nm2),已经比较高效了,但是按照dalao的思路,可以用前缀最小值优化 k k k的时间,优化至 O ( n m ) O(nm) O(nm)


代码(线性dp)

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
#define min(a,b) (((a)<(b))?(a):(b))
using namespace std;
int dp[2][201],ans=1e8,n,m,a[501];
inline int iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
    return ans;
}
int main(){
    n=iut(); m=iut();
    for (rr int i=1;i<=n;++i) a[i]=iut();
    sort(a+1,a+1+n);
    for (rr int i=1;i<=n;++i){
    	dp[i&1][0]=1e8;
    	for (rr int j=0;j<=min(a[i]-a[i-1]-m,m-1);++j) dp[i&1][0]=min(dp[i&1][0],dp[1-(i&1)][j]);
        for (rr int j=1;j<2*m;++j){
        	dp[i&1][j]=dp[i&1][j-1];//前缀最小值
        	if (a[i]-a[i-1]+j>=m&&a[i]-a[i-1]+j<3*m)
            dp[i&1][j]=min(dp[i&1][j],dp[1-(i&1)][a[i]-a[i-1]+j-m]);//新开一段
        }
        for (rr int j=0;j+a[i]-a[i-1]<2*m;++j)
            dp[i&1][j]=min(dp[i&1][j],dp[1-(i&1)][j+a[i]-a[i-1]]);//同一段
        for (rr int j=0;j<2*m;++j) dp[i&1][j]+=j;//无论开不开都要加等待时间
    }
    for (rr int i=0;i<m;++i) ans=min(ans,dp[n&1][i]);//取最小值
    return !printf("%d",ans);
}

分析(斜率优化)

斜率优化与上面的线性dp迥然不同,而且对于 O ( n m ) O(nm) O(nm)这么小的情况下, O ( m a x { t i } ) O(max\{t_i\}) O(max{ti})太大了,不过为了培养更多的思维,在此讲一下斜率优化的方法(肯定非高效正解)
d p [ i ] dp[i] dp[i]表示到第 i i i时刻的总等待时间, c n t [ i ] cnt[i] cnt[i]表示到第 i i i时刻的同学个数, s u m [ i ] sum[i] sum[i]表示在第 i i i时刻时发车的总等待时间
那么 d p [ i ] = m i n { d p [ j ] + ( c n t [ i ] − c n t [ j ] ) ∗ i − ( s u m [ i ] − s u m [ j ] ) } dp[i]=min\{dp[j]+(cnt[i]-cnt[j])*i-(sum[i]-sum[j])\} dp[i]=min{dp[j]+(cnt[i]cnt[j])i(sum[i]sum[j])}
然而这样肯定会超时,若使 k ( j &lt; k ) k(j&lt;k) k(j<k)更优秀,那么
d p [ k ] + ( c n t [ i ] − c n t [ k ] ) ∗ i − ( s u m [ i ] − s u m [ k ] ) &lt; d p [ j ] + ( c n t [ i ] − c n t [ j ] ) ∗ i − ( s u m [ i ] − s u m [ j ] ) dp[k]+(cnt[i]-cnt[k])*i-(sum[i]-sum[k])&lt;dp[j]+(cnt[i]-cnt[j])*i-(sum[i]-sum[j]) dp[k]+(cnt[i]cnt[k])i(sum[i]sum[k])<dp[j]+(cnt[i]cnt[j])i(sum[i]sum[j])
根据一次函数 y = k x + b y=kx+b y=kx+b可以得到
d p [ k ] − d p [ j ] + s u m [ k ] − s u m [ j ] c n t [ k ] − c n t [ j ] &lt; i \frac{dp[k]-dp[j]+sum[k]-sum[j]}{cnt[k]-cnt[j]}&lt;i cnt[k]cnt[j]dp[k]dp[j]+sum[k]sum[j]<i
因为 i i i是单调递增的,所以说需要维护下凸壳


代码(斜率优化)

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
#define min(a,b) (((a)<(b))?(a):(b))
using namespace std;
int dp[4000201],ans=1e8,n,m,T,cnt[4000201],sum[4000201],l=1,r,q[4000201];
inline int iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
    return ans;
}
signed main(){
    n=iut(); m=iut();
    for (rr int i=1;i<=n;++i){
    	rr int x=iut(); T=max(T,x);
    	++cnt[x]; sum[x]+=x;
    }
    for (rr int i=1;i<=T+m;++i) cnt[i]+=cnt[i-1],sum[i]+=sum[i-1];
    for (rr int i=0;i<m-1;++i) dp[i]=cnt[i]*i-sum[i];
    for (rr int i=m-1;i<=T+m;++i){
        while (l<r&&(dp[q[l+1]]-dp[q[l]]+sum[q[l+1]]-sum[q[l]])<=i*(cnt[q[l+1]]-cnt[q[l]])) ++l;//排除队头
        dp[i]=cnt[i]*i-sum[i];//特殊情况
        if (l<=r) dp[i]=min(dp[i],dp[q[l]]+(cnt[i]-cnt[q[l]])*i-(sum[i]-sum[q[l]]));//正常的dp方程
        while (l<r&&(dp[q[r]]-dp[q[r-1]]+sum[q[r]]-sum[q[r-1]])*(cnt[i-m+1]-cnt[q[r]])>=(dp[i-m+1]-dp[q[r]]+sum[i-m+1]-sum[q[r]])*(cnt[q[r]]-cnt[q[r-1]])) --r;//排除队尾
        q[++r]=i-m+1;//只有得到第i个,才能插入i-m+1
    }
    for (rr int i=T;i<=T+m;++i) ans=min(ans,dp[i]);
    return !printf("%d",ans);
}

洛谷 5018 对称二叉树

题目链接

分析

一开始我以为暴力过不了,没想到时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn),结果成功垫底(可能是因为均摊的原因)
中序+manacher我不会


代码

#include <cstdio>
#include <cctype>
#define rr register
#define max(a,b) (((a)>(b))?(a):(b))
using namespace std;
const int N=1000000;
int n,a[N+1],lson[N+1],rson[N+1],ans;
inline signed iut(){
    rr int ans=0,f=1; rr char c=getchar();
    while (!isdigit(c)&&c!='-') c=getchar();
    if (c=='-') c=getchar(),f=-f;
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
    return ans*f;
}
inline signed check(int x,int y){
    if (x==-1&&y==-1) return 1;
    if (a[x]!=a[y]) return 0;
    return check(rson[x],lson[y])&&check(lson[x],rson[y]);
}
inline signed dfs(int x){
    rr int siz=1;
    if (lson[x]!=-1) siz+=dfs(lson[x]);
    if (rson[x]!=-1) siz+=dfs(rson[x]);
    if (check(lson[x],rson[x])) ans=max(ans,siz);
    return siz;
}
signed main(){
    scanf("%d",&n);
    for (rr int i=1;i<=n;++i) scanf("%d",&a[i]);
    for (rr int i=1;i<=n;++i) scanf("%d%d",&lson[i],&rson[i]);
    dfs(1);
    printf("%d",ans);
    return 0;
}

后续

突然发现第一第二题全对第四题输出1就可以在广东拿一等奖了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值