NOIP2024加赛5

NOIP2024加赛5

题目来源: 2023NOIP A层联测31

T1 HZTG5777. 暴力操作(opt) 40pts

  • 先将 {a} 升序排序。

  • 因为 abc=abc ,先钦定 i,jN,ci,jci+cj ,并让每个数仅被操作一遍。

  • 考虑二分答案,并只对 [1,n2] 进行操作。

  • 此时需要解形如 aixmid 的不等式,可以使用二分求解,也可以观察到 aix<mid+1 从而得到 xaimid+1+1 。然后取 mink[aimid+1+){cx} 加入代价。

  • 对于 mink[aimid+1+){cx} 初始时仅通过调和级数处理到 m 是不够的。

  • {suf} 表示 {c} 的后缀 min 。不妨先处理出 [1,m] 的后缀 min ,此时有 sufm+1=mini=1m{ci+sufm+1i} ,再次倒着更新 [1,m+1] 的后缀 min 即可。

    点击查看代码
    ll a[500010],c[500010],suf[500010];
    bool check(ll n,ll m,ll k,ll mid)
    {
        ll sum=0;
        for(ll i=1;i<=n/2+1;i++)
        {
            if(a[i]>mid)
            {
                sum+=suf[a[i]/(mid+1)+1];
            }
        }
        return sum<=k;
    }	
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("opt.in","r",stdin);
        freopen("opt.out","w",stdout);
    #endif	
        ll n,m,k,l=0,r,mid,ans=-1,i,j;
        scanf("%lld%lld%lld",&n,&m,&k);
        r=m;
        for(i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
        }
        for(i=1;i<=m;i++)
        {
            scanf("%lld",&c[i]);
        }
        sort(a+1,a+1+n);
        c[1]=0;
        for(i=1;i<=m;i++)
        {
            for(j=1;i*j<=m;j++)
            {
                c[i*j]=min(c[i*j],c[i]+c[j]);
            }
        }
        suf[m]=c[m];
        for(i=m-1;i>=1;i--)
        {
            suf[i]=min(suf[i+1],c[i]);
        }
        suf[m+1]=0x3f3f3f3f;
        for(i=1;i<=m;i++)
        {
            j=ceil(1.0*(m+1)/i);
            if(j<=m)
            {
                suf[m+1]=min(suf[m+1],c[i]+suf[j]);
            }
        }
        for(i=m;i>=1;i--)
        {
            suf[i]=min(suf[i+1],c[i]);
        }
        while(l<=r)
        {
            mid=(l+r)/2;
            if(check(n,m,k,mid)==true)
            {
                ans=mid;
                r=mid-1;
            }
            else
            {
                l=mid+1;
            }
        }
        printf("%lld\n",ans);
        return 0;
    } 
    

T2 HZTG5778. 异或连通(xor) 50pts

  • 部分分

    • 子任务 1DFS 找连通块。
    • 子任务 2 :加个记忆化即可。
    • 子任务 3 :使用并查集找连通块。
    点击查看代码
    ll u[200010],v[200010],w[200010];
    unordered_map<ll,ll>f;
    struct DSU
    {
        ll fa[200010],siz[200010];
        void init(ll n)
        {
            for(ll i=1;i<=n;i++)
            {
                fa[i]=i;
                siz[i]=1;
            }
        }
        ll find(ll x)
        {
            return fa[x]==x?x:fa[x]=find(fa[x]);
        }
        void merge(ll x,ll y)
        {
            x=find(x);
            y=find(y);
            if(x!=y)
            {
                fa[y]=x;
                siz[x]+=siz[y];
            }
        }
    }D;
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("xor.in","r",stdin);
        freopen("xor.out","w",stdout);
    #endif	
        ll n,m,q,k,x,ans=0,i,j;
        scanf("%lld%lld%lld%lld",&n,&m,&q,&k);
        for(i=1;i<=m;i++)
        {
            scanf("%lld%lld%lld",&u[i],&v[i],&w[i]);
        }
        for(j=1;j<=q;j++)
        {
            scanf("%lld",&x);
            if(f.find(x)==f.end())
            {
                ans=0;
                D.init(n);
                for(i=1;i<=m;i++)
                {
                    if((w[i]^x)<k)
                    {
                        D.merge(u[i],v[i]);
                    }
                }
                for(i=1;i<=n;i++)
                {
                    if(D.fa[i]==i)
                    {
                        ans+=D.siz[i]*(D.siz[i]-1)/2;
                    }
                }
                f[x]=ans;
            }
            printf("%lld\n",f[x]);
        }
        return 0;
    }
    
  • 正解

    • 观察到每条边存在时对应的 k 至多形成 (logV) 段极长连续区间,使用 01Trie 求出区间并离散化后跑线段树分治即可。
    点击查看代码
    ll u[200010],v[200010],w[200010],a[200010],b[200010],ans[200010];
    struct quality
    {
        ll id,fa,siz;	
    };
    struct DSU
    {
        ll fa[200010],siz[200010];
        void init(ll n)
        {
            for(ll i=1;i<=n;i++)
            {
                fa[i]=i;
                siz[i]=1;
            }
        }
        ll find(ll x)
        {
            return fa[x]==x?x:find(fa[x]);
        }
        void merge(ll x,ll y,stack<quality>&s,ll &sum)
        {
            x=find(x);
            y=find(y);
            if(x!=y)
            {
                s.push((quality){x,fa[x],siz[x]});
                s.push((quality){y,fa[y],siz[y]});
                if(siz[x]<siz[y])
                {
                    swap(x,y);
                }
                fa[y]=x;
                sum-=siz[y]*(siz[y]-1)/2;
                sum-=siz[x]*(siz[x]-1)/2;
                siz[x]+=siz[y];
                sum+=siz[x]*(siz[x]-1)/2;
            }
        }
        void split(stack<quality>&s)
        {
            while(s.empty()==0)
            {
                fa[s.top().id]=s.top().fa;
                siz[s.top().id]=s.top().siz;
                s.pop();
            }
        }
    }D;
    struct SMT
    {
        struct SegmentTree
        {
            vector<ll>info;
        }tree[800010];
        ll lson(ll x)
        {
            return x*2;
        }
        ll rson(ll x)
        {
            return x*2+1;
        }
        void update(ll rt,ll l,ll r,ll x,ll y,ll id)
        {
            if(l>y||r<x)
            {
                return;
            }
            if(x<=l&&r<=y)
            {
                tree[rt].info.push_back(id);
                return;
            }
            ll mid=(l+r)/2;
            update(lson(rt),l,mid,x,y,id);
            update(rson(rt),mid+1,r,x,y,id);
        }
        void solve(ll rt,ll l,ll r,ll sum)
        {	
            stack<quality>s;
            ll mid=(l+r)/2;
            for(ll i=0;i<tree[rt].info.size();i++)
            {
                D.merge(u[tree[rt].info[i]],v[tree[rt].info[i]],s,sum);
            }
            if(l==r)
            {
                ans[l]=sum;
            }
            else
            {
                solve(lson(rt),l,mid,sum);
                solve(rson(rt),mid+1,r,sum);
            }
            D.split(s);
        }
    }S;
    struct Trie
    {
        ll son[6000010][2],st[6000010],ed[6000010],rt_sum=0;
        void insert(ll s,ll id,ll k)
        {
            ll x=0;
            for(ll i=30;i>=0;i--)
            {
                if((k>>i)&1)
                {
                    S.update(1,1,b[0],st[son[x][(s>>i)&1]],ed[son[x][(s>>i)&1]],id);
                    x=son[x][((s>>i)&1)^1];
                }
                else
                {
                    x=son[x][(s>>i)&1];
                }
                if(x==0)
                {
                    break;
                }
            }
        }
        void query(ll s,ll id)
        {
            ll x=0;
            for(ll i=30;i>=0;i--)
            {
                if(son[x][(s>>i)&1]==0)
                {
                    rt_sum++;
                    son[x][(s>>i)&1]=rt_sum;
                    st[son[x][(s>>i)&1]]=id;
                }
                x=son[x][(s>>i)&1];
                ed[x]=id;
            }
        }
    }T;
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("xor.in","r",stdin);
        freopen("xor.out","w",stdout);
    #endif	
        ll n,m,q,k,i;
        scanf("%lld%lld%lld%lld",&n,&m,&q,&k);
        for(i=1;i<=m;i++)
        {
            scanf("%lld%lld%lld",&u[i],&v[i],&w[i]);
        }
        for(i=1;i<=q;i++)
        {
            scanf("%lld",&a[i]);
            b[i]=a[i];
        }
        sort(b+1,b+1+q);
        b[0]=unique(b+1,b+1+q)-(b+1);
        for(i=1;i<=b[0];i++)
        {
            T.query(b[i],i);
        }
        for(i=1;i<=m;i++)
        {
            T.insert(w[i],i,k);
        }
        D.init(n);
        S.solve(1,1,b[0],0);
        for(i=1;i<=q;i++)
        {
            cout<<ans[lower_bound(b+1,b+1+b[0],a[i])-b]<<endl;
        }
        return 0;
    }
    

T3 HZTG5779. 诡异键盘(keyboard) 20pts

  • 部分分

    • 子任务 1 :哈希优化字符串匹配后跑 O(n3)DP
    点击查看代码
    const ll mod=1000003579,base=13331;
    ll f[5010],hshs[5010],lent[5010],jc[5010];
    vector<ll>hsht[5010];
    char s[1000010];
    void sx_hash(char s[],ll len)
    {
        for(ll i=0;i<=len;i++)
        {
            hshs[i]=(i==0)?0:(hshs[i-1]*base%mod+s[i])%mod;
        }
    }
    void sx_hash_t(char s[],ll len,ll id)
    {
        for(ll i=0;i<=len;i++)
        {
            hsht[id][i]=(i==0)?0:(hsht[id][i-1]*base%mod+s[i])%mod;
        }
    }
    ll ask_hash(ll l,ll r)
    {
        return (hshs[r]-hshs[l-1]*jc[r-l+1]%mod+mod)%mod;
    }
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("keyboard.in","r",stdin);
        freopen("keyboard.out","w",stdout);
    #endif	
        int t,n,m,len,i,j,k,h;
        cin>>t;
        for(i=0;i<=5000;i++)
        {
            jc[i]=(i==0)?1:jc[i-1]*base%mod;
        }
        for(h=1;h<=t;h++)
        {
            memset(f,0x3f,sizeof(f));
            cin>>n>>m;
            for(i=1;i<=n;i++)
            {
                cin>>(s+1);
                lent[i]=strlen(s+1);
                hsht[i].clear();
                hsht[i].resize(lent[i]+10);
                sx_hash_t(s,lent[i],i);
            }
            cin>>(s+1);
            len=strlen(s+1);
            sx_hash(s,len);
            f[0]=0;
            for(i=1;i<=len;i++)
            {
                for(j=0;j<=i-1;j++)
                {
                    for(k=1;k<=n;k++)
                    {
                        if(i-j<=lent[k]&&ask_hash(j+1,i)==hsht[k][i-j])
                        {
                            f[i]=min(f[i],f[j]+lent[k]-(i-j)+1);
                        }
                    }
                }
            }
            cout<<((f[len]==0x3f3f3f3f3f3f3f3f)?1:f[len])<<endl;
        }
        return 0;
    }
    
  • 正解

T4 HZTG5780. 民主投票(election) 5pts

  • 部分分

    • 子任务 1 :模拟。
    点击查看代码
    vector<int>e[1000010];
    int siz[1000010],dfn[1000010],out[1000010],pos[1000010],fa[1000010],sum[1000010],tot;
    void add(int u,int v)
    {
        e[u].push_back(v);
    }
    void dfs(int x)
    {
        siz[x]=1;
        tot++;
        dfn[x]=tot;
        pos[tot]=x;
        for(int i=0;i<e[x].size();i++)
        {
            dfs(e[x][i]);
            siz[x]+=siz[e[x][i]];
        }
        out[x]=tot;
    }
    bool work(int x,int maxx)
    {
        if(x==1)
        {
            return true;
        }
        x=fa[x];
        while(x!=0)
        {
            if(sum[x]+1<maxx)
            {
                sum[x]++;
                return true;
            }
            x=fa[x];
        }
        return false;
    }
    int check(int x,int n)
    {
        memset(sum,0,sizeof(sum));
        int maxx=siz[x]-1;
        for(int i=1;i<=n;i++)
        {
            if((i<=dfn[x]||i>out[x])&&work(pos[i],maxx)==false)
            {
                return 0;
            }
        }
        return 1;
    }
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("election.in","r",stdin);
        freopen("election.out","w",stdout);
    #endif	
        int t,n,i,j,k;
        scanf("%d",&t);
        for(k=1;k<=t;k++)
        {
            scanf("%d",&n);
            tot=0;
            for(i=1;i<=n;i++)
            {
                e[i].clear();
            }
            for(i=2;i<=n;i++)
            {
                scanf("%d",&fa[i]);
                add(fa[i],i);
            }
            dfs(1);
            for(i=1;i<=n;i++)
            {
                printf("%d",check(i,n));
            }
            printf("\n");
        }
        return 0;
    }
    
  • 正解

    • 考虑先二分出树上最少的最多次数 k 。具体地,设当前二分出的答案为 midcheck 时设 fx,mid 表示以 x 为根的子树内除去 x 在满足每个人最多被投 mid 票时,需要向上投的最少票数,状态转移方程为 fx,mid=max(0,ySon(x)(fy,mid+1)mid) 。最后答案合法当且仅当 f1,mid=0
    • 难点在于最后询问满足 sizx1=kx 时其他节点是否能满足最多 sizx2=k1 张票的限制条件。不难发现 fx,k11 ,若 xx,k1=0 因为 f1,mid>0x 不会获胜,否则若 y(1x),fy,k1=1x 获得 k 张票时有 y(1fax),fy,k1=0x 会获胜。
    • 略带卡常。
    点击查看代码
    vector<int>e[1000010];
    int fa[1000010],siz[1000010],ans[1000010],f[1000010],dfn[1000010],pos[1000010],tot;
    void add(int u,int v)
    {
        e[u].push_back(v);
    }
    void dfs1(int x)
    {
        siz[x]=1;
        tot++;
        dfn[x]=tot;
        pos[tot]=x;
        for(int i=0;i<e[x].size();i++)
        {
            dfs1(e[x][i]);
            siz[x]+=siz[e[x][i]];
        }
    }
    void dfs2(int n,int mid)
    {
        for(int i=1;i<=n;i++)
        {
            f[i]=0;
        }
        for(int i=n;i>=1;i--)
        {
            f[pos[i]]=max(0,f[pos[i]]-mid);
            f[fa[pos[i]]]+=f[pos[i]]+1;
        }
    }
    void dfs3(int x)
    {
        if((f[x]==0)||(x==1&&f[x]!=1))
        {
            return;
        }
        ans[x]=1;
        for(int i=0;i<e[x].size();i++)
        {
            dfs3(e[x][i]);
        }
    }
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("election.in","r",stdin);
        freopen("election.out","w",stdout);
    #endif	
        int t,n,l,r,k,mid,i,j;
        scanf("%d",&t);
        for(j=1;j<=t;j++)
        {
            scanf("%d",&n);
            tot=0;
            for(i=1;i<=n;i++)
            {
                e[i].clear();
                ans[i]=0;
            }
            for(i=2;i<=n;i++)
            {
                scanf("%d",&fa[i]);
                add(fa[i],i);
            }
            dfs1(1);
            l=1;
            r=n;
            k=-1;
            while(l<=r)
            {
                mid=(l+r)/2;
                dfs2(n,mid);
                if(f[1]==0)
                {
                    k=mid;
                    r=mid-1;
                }
                else
                {
                    l=mid+1;
                }
            }
            dfs2(n,k-1);
            dfs3(1);
            for(i=1;i<=n;i++)
            {
                printf("%d",((siz[i]-1==k)?ans[i]:(siz[i]-1>k)));
            }
            printf("\n");
        }
        return 0;
    }
    

总结

  • T1 调和级数仅处理到了 m ,挂了 60pts
  • T4 链的部分分写假了,挂了 20pts

后记

  • T1 题面从 tgHZOJ 搬过来的时候忘把更改后的题面(将原 x[1,m] 改成了 cx[1,m])搬过来了,赛时 feifei 用画图更改了题面重新下发了。
  • T3 未注明 |Si| 为每个数据点的总数据规模,赛时 feifei 还称是单组数据的数据规模。
posted @   hzoi_Shadow  阅读(151)  评论(7编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
扩大
缩小
点击右上角即可分享
微信分享提示