• [NOIP模拟26]题解


    今天的考试题改自闭了……所以滚来写陈年题解。

    A.*****贪婪*****

    RT,出题人告诉我们这题要贪心。

    最优的策略一定是拖到必须断的时候再断开(虽然并不知道为什么)。

    如果一段序列满足题目中的性质,那么一定有$gcd(a_i-a_{i-1},a_{i+1}-a_i,...)$不为1且$a_i,a_{i+1},...$各不相同。所以维护每段的相邻两项差值的gcd,遇到不符合或者重复的元素就ans++。set写起来比较方便。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<set>
    #include<cmath>
    using namespace std;
    const int N=100005;
    int n,a[N],ans=1;
    set<int> b;
    int gcd(int x,int y)
    {
        if(!y)return x;
        return gcd(y,x%y);
    }
    int now;
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        b.insert(a[1]);
        for(int i=2;i<=n;i++)
        {
            now=gcd(now,abs(a[i]-a[i-1]));
            if(now==1||b.count(a[i]))
            {
                ans++;
                now=0;
                b.clear();b.insert(a[i]);
            }
            else b.insert(a[i]);
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code

    B.**见证*******

    RT,出题人让我们见证$n^2$过500000。

    没有打std中的离线dfs序解法,直接用有向边代表包含关系,每次询问暴力dfs统计答案即可。注意K=1要特判建双向边。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<vector>
    using namespace std;
    int n,m,cnt;
    const int N=500005;//change it!
    int head[N],nxt[N],to[N],tot,vis[N];
    void add(int x,int y)
    {
        to[++tot]=y;
        nxt[tot]=head[x];
        head[x]=tot;
    }
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    bool dfs(int x,int f,int aim)
    {
        if(x==aim)return 1;
    
        for(int i=head[x];i;i=nxt[i])
        {
            if(to[i]!=f)
                if(dfs(to[i],x,aim))return 1;
        }
        return 0;
    }
    int main()
    {
        n=read();m=read();cnt=n;
        while(m--)
        {
            int opt=read();
            if(!opt)
            {
                int op=read(),K=read(),now=++cnt;
                if(K==1)
                {
                    int tmp=read();
                    add(now,tmp);add(tmp,now);
                }
                else if(op==0)
                {
                    for(int i=1;i<=K;i++)
                        add(now,read());    
                }
                else if(op==1)
                {
                    for(int i=1;i<=K;i++)
                        add(read(),now);
                }
            }
            else if(opt==1)
            {
                int x=read(),y=read();
    
    
                printf("%d
    ",dfs(x,0,y));
            }
        }    
        return 0;
    }
    View Code

    C.**堆积******

    RT,出题人告诉我们要用到堆。

    暴力dp的方程可以一眼切掉:$dp[i]=min limits _{j=max(0,i-K)}^{i-1} dp[j]+max(sum[i]-sum[j],b[j])$

    对于所有j,$dp[j]+b[j]$和$dp[j]-sum[j]$都是定值

    所以用两个堆,分别维护二者。要求的$dp[i]=min(heap1.top,heap2.top+sum[i])$

    首先检查第一个栈,如果取出的j比i-K小,pop掉继续取。如果取出的比$dp[j]-sum[j]+sum[i]$小,把它加进第二个堆里,pop掉。

    就这样取到合法的为止,第二个堆也是一样。

    时间复杂度$O(nlogn)$

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<queue>
    using namespace std;
    typedef long long ll;
    #define pa pair<ll,int>
    int n,K;
    const int N=500005;
    int a[N],b[N];
    ll sum[N],dp[N];
    priority_queue<pa,vector<pa>,greater<pa> >q1,q2;
    int main()
    {
        scanf("%d%d",&n,&K);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]),sum[i]=sum[i-1]+1LL*a[i];
        for(int i=0;i<n;i++)
            scanf("%d",&b[i]);
        memset(dp,0x3f,sizeof(dp));
        dp[0]=0;
        q1.push(make_pair(dp[0]+1LL*b[0],0));
        for(int i=1;i<=n;i++)
        {
            ll val1=0x7ffffffffff,val2=0x7ffffffffff;
            while(!q1.empty())
            {
                ll val=q1.top().first;int id=q1.top().second;
                if(id<i-K)
                {
                    q1.pop();continue;
                }
                if(val<dp[id]-sum[id]+sum[i])
                {
                    q2.push(make_pair(dp[id]-sum[id],id));q1.pop();
                    continue;
                }
                val1=min(val1,val);
                break;
                
            }
    
            while(!q2.empty())
            {
                ll val=q2.top().first;
                int id=q2.top().second;
                if(id<i-K)
                {
                    q2.pop();continue;
                }
                val2=min(val2,val);
                break;
    
    
            }
            dp[i]=min(val1,val2+sum[i]);
            //cout<<i<<' '<<dp[i]<<endl;
            q1.push(make_pair(dp[i]+b[i],i));
        }
        cout<<dp[n]<<endl;
        return 0;
    }
    View Code
  • 相关阅读:
    java中的单例模式
    数组的冒泡排序
    2019年总结—即将而立之年的90后
    圣诞节开启博客之旅
    分布式多线程的Lock示例
    抽象工厂模式
    观察者模式
    建造者模式
    外观模式(Facade)
    模板方法模式
  • 原文地址:https://www.cnblogs.com/Rorschach-XR/p/11391241.html
Copyright © 2020-2023  润新知