CSP+NOIP总结

CSP

普及:
自认为这次考的是很不理想的,因为平时的习惯,连桶排这种基础算法都没有掌握,排序到考前为止只会sort,这就导致了与省一无缘
T3直接就放弃了,当时只想着T4如果能打出来就有省一了
且T4也分析错误了,没有想到分类来讨论从上往下走和从下往上走的情况,以至于只打了爆搜,10tps
如果考前能够再老老实实复习一遍基本的排序和DP,也许结果就大不一样了
这次也是给自己敲响了警钟,因为如果还不能调整好状态的话跟别人的差距可能就越来越大了


提高:
T1直接放弃
然后再打T2和T3的暴力
事实证明以上策略是很有效的
T4如果不那么贪心,直接讨论n=3恐怕还能再多得20tps
感觉自己离提高组的差距还是太大了,没有掌握的东西还有很多


题解部分:
普及:

1.优秀的拆分

本来是一道二进制的题,但考场上是用快速幂做的
有个坑点是必须从大到小枚举,因为如果从小到大再从大到小可能凑不出来n
且n为奇时肯定凑不出来,因为必须用到2的0次方,不符合要求
code:

#include<cstdio>
#include<iostream>
using namespace std;
int ksm(int a,int b)
{
   
    int ans=1;
    while(b)
    {
   
        if(b&1)
        {
   
            ans=ans*a;
        }
        a=a*a;
        b>>=1;
    }
    return ans;
}
int main()
{
   
    int n;
    scanf("%d",&n);
    if(n%2!=0)
    {
   
        printf("-1");
        return 0;
    }
    while(n)
    {
   
        for(int i=24;i>=1;i--)//1e7<2^24
        {
   
            if(ksm(2,i)<=n){
   
                printf("%d ",ksm(2,i));
                n-=ksm(2,i);
                break;
            }
        }
    }
    return 0;
}

2.直播获奖

本题目的考点在于需要实时进行排序,而sort的时间复杂度为n,显然n^2的时间复杂度是过不了这道题的
那么我们观察到w[i]<=600,也就是说我们可以把每个w[i]放到一个数组a里面,a[w[i]]的个数就是当前得分为w[i]的人数,这时我们从600往1查找,如果当前总人数>=max( i ∗ w i*w iw%,1),就代表已经到了排名为max( i ∗ w i*w iw%,1)的值,输出即可
时间复杂度为O(600*n)
code:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,a[1000005],w,t[605];
int main()
{
   
    scanf("%d%d",&n,&w);
    for(int i=1;i<=n;i++)
    {
   
        scanf("%d",&a[i]);
        int p=max(1,i*w/100),now=0;//now为当前总人数
        t[a[i]]++;
        for(int j=600;i>=0;j--){
   
            now+=t[j];
            if(now>=p)
            {
   
                printf("%d ",j);
                break;
            }
        }
    }
    return 0;
}

3.表达式

为了解决这道题,我们需要引入一个知识点:表达式树
通俗来说,其实就是通过栈来将后缀表达式的数字和符号建一颗子节点为数字或字符,父节点为字符的树
样例一的表达式树就如下图
在这里插入图片描述
而一颗子树的值,就是当前子树的左子树的值和当前子树的右子树的值进行当前子树根节点的操作后的值
又因为&运算结果为1时,必须两个都为1,那么只要有当前子树的一颗子树的取值为0,另一颗不论取何值,都无法对当前子树的结果产生影响
又因为&运算结果为1时,只要一个为1即可,那么只要有当前子树的一颗子树的取值为1,另一颗不论取何值,都无法对当前子树的结果产生影响
即我们可以用一个数组is来表示当前结点的值取反后是否会对所在当前结点父亲为根节点的子树的值造成影响
其中is[x]=1表示没有影响,is[x]=0表示有影响
另一点需要注意的是,如果当前结点的父节点取反对当前子树的取值没有影响,那么无论当前结点取反是否会影响到以当前结点的子树的值,都不会对当前子树的取值产生影响
换句话说,我们可以通过下传每一个父亲节点对当前子树有无影响从而判断取反该节点有无影响
例如当前节点的is为0,而其父亲结点的is为1
那么我们将当前节点的is|父亲节点的is,这个节点的is就变成了1
而如果其父亲节点的is为0,那么说明父亲节点的取反肯定会对当前子树造成影响,那么它的取反既然也能对当前子树产生影响,那么肯定取反他会影响当前子树
又因为每次取反的元素只有一个,肯定不可能出现它与它父亲同时取反的情况,那么当前点的is如果为0,肯定对最终的答案会造成影响
即,当前节点取反对父亲造成影响,父亲取反又对父亲的父亲造成影响。。。
那么最终如果根节点为0的话一定会对根节点产生影响(否则标记下传时有一个父亲节点为1,那么肯定不会改变)
所以这个递归就从根节点|0开始(|1的话会导致所有结点都不会产生影响)
code:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
char S[1000005];
int n,od[1000005];<
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值