多校A层冲刺NOIP2024模拟赛25

多校A层冲刺NOIP2024模拟赛25

\(T1\) A. 图 \(100pts/100pts\)

  • 对于每个状态开一个 bitset 加速位运算即可。

    点击查看代码
    bitset<10010>vis[10010],tmp[4];
    char s[10010];
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
    #endif
        int n,m,ans=0,i,j,k;
        cin>>n>>m;
        for(k=1;k<=m;k++)
        {
            cin>>(s+1);
            for(i=1;i<=3;i++)
            {
                tmp[i].reset();
            }
            for(i=n;i>=1;i--)
            {
                if(s[i]=='1')
                {
                    vis[i]^=tmp[2];
                    vis[i]^=tmp[3];
                }
                if(s[i]=='2')
                {
                    vis[i]^=tmp[1];
                    vis[i]^=tmp[3];
                }
                if(s[i]=='3')
                {
                    vis[i]^=tmp[1];
                    vis[i]^=tmp[2];
                    vis[i]^=tmp[3];
                }
                tmp[s[i]-'0'][i]=1;
            }
        }
        for(i=1;i<=n;i++)
        {
            ans+=vis[i].count();
        }
        cout<<ans<<endl;
        return 0;
    }
    

\(T2\) B. 序列 \(0pts/0pts\)

  • 考虑将所有操作表示成 \(a_{i}\) 乘以一个系数的形式。

    • 赋值仅考虑赋值后的最大值 \(y_{max}\) ,将其转换成 \(y_{max}-a_{i}\) 的形式(先不用管正负号的问题)。
    • 加一定是先加较大的数再加较小的数,不妨先降序排序,其贡献为 \(\dfrac{a_{i}'+y_{t}}{a_{i}'}\) ,其中 \(a_{i}'=a_{i}+\sum\limits_{j=1}^{t-1}y_{j}\)
    • 乘直接处理即可。
  • 将贡献扔到操作序列上降序排序即可。

  • 因为可能存在 \(a_{i}' \equiv 0 \pmod {10000000007}\) 的情况,所以可以把答案中的 \(10^{9}+7\) 的指数单独提出去(另外可以证明只会非负整数)。

    点击查看代码
    const ll p=1000000007;
    ll a[100010],maxx[100010],last[100010];
    vector<ll>sum[100010];
    vector<pair<__int128_t,__int128_t> >c;
    bool cmp(pair<__int128_t,__int128_t>a,pair<__int128_t,__int128_t>b)
    {
        return a.first*b.second>b.first*a.second;
    }
    ll qpow(ll a,ll b,ll p)
    {
        ll ans=1;
        while(b)
        {
            if(b&1)
            {
                ans=ans*a%p;
            }
            b>>=1;
            a=a*a%p;
        }
        return ans;
    }
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("b.in","r",stdin);
        freopen("b.out","w",stdout);
    #endif
        ll n,m,t,x,y,ans=1,cnt=0,i,j;
        cin>>n>>m;
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
            ans=ans*a[i]%p;
        }
        for(i=1;i<=m;i++)
        {
            cin>>t>>x>>y;
            if(t==1)
            {
                maxx[x]=max(maxx[x],y);
            }
            if(t==2)
            {
                sum[x].push_back(y);
            }
            if(t==3)
            {
                c.push_back(make_pair(y,1));
            }
        }
        for(i=1;i<=n;i++)
        {
            if(maxx[i]>a[i])
            {
                sum[i].push_back(maxx[i]-a[i]);
            }
            sort(sum[i].begin(),sum[i].end(),greater<ll>());
            for(j=0;j<sum[i].size();j++)
            {
                c.push_back(make_pair(a[i]+sum[i][j],a[i]));
                a[i]+=sum[i][j];
            }
        }
        sort(c.begin(),c.end(),cmp);
        for(i=0;i<=m;i++)
        {
            cout<<(cnt==0)*ans<<" ";
            if(i<c.size())
            {
                if(c[i].first%p==0)
                {
                    c[i].first/=p;
                    cnt++;
                }
                if(c[i].second%p==0)
                {
                    c[i].second/=p;
                    cnt--;
                }
                ans=(ans*(c[i].first%p)%p)*qpow(c[i].second%p,p-2,p)%p;
            }
        }
        return 0;
    }
    

\(T3\) C. 树 \(10pts/0pts\)

  • 部分分

    • \(20pts\) :暴力连边然后进行博弈,时间复杂度为 \(n^{2d+1}d\)
    点击查看代码
    const ll p=1000000007;
    int ans;
    vector<int>e[8000010];
    void add(int u,int v)
    {
        e[u].push_back(v);
    }
    int ask(int x,int fa)
    {
        for(int i=0;i<e[x].size();i++)
        {
            if(e[x][i]!=fa&&ask(e[x][i],x)==0)
            {
                return 1;
            }
        }
        return 0;
    }
    void dfs(int pos,int d,int n)
    {
        if(pos==d+1)
        {
            ans=(ans+ask(1,0))%p;
        }
        else
        {
            for(int i=1+(pos-1)*n;i<=pos*n;i++)
            {
                for(int j=1+pos*n;j<=(pos+1)*n;j++)
                {
                    e[i].push_back(j);
                    dfs(pos+1,d,n);
                    e[i].pop_back();
                }
            }
        }
    }
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("c.in","r",stdin);
        freopen("c.out","w",stdout);
    #endif
        int n,d,u,v,i,j;
        cin>>n>>d;
        for(j=1;j<=n-1;j++)
        {
            cin>>u>>v;
            for(i=0;i<=d;i++)
            {
                add(u+i*n,v+i*n);
                add(v+i*n,u+i*n);
            }
        }
        dfs(1,d,n);
        cout<<ans<<endl;
        return 0;
    }
    
  • 正解

\(T4\) D. 字符串 \(0pts/0pts\)

  • 部分分

    • \(10pts\) :双指针判断是否需要插入,写法和判断一个数列是否是另一个数列的子序列差不多。
  • 正解

    • 对每个字符按照 \(t\) 中位置进行编号,然后等价于查询 \([l,r]\) 中极长严格递增段的个数加 \(1\) 。考虑统计断点(相邻两个字符逆序)的个数。
    • 观察到 \(k \in [1,10]\) ,所以直接对着值域挂在线段树的每个节点上就行了。
    点击查看代码
    int k,tmp[11][11],id[11];
    char s[200010],t[25];
    struct SMT
    {
        struct SegmentTree
        {
            int lc,rc,sum[11][11],lazy;
            SegmentTree()
            {
                lc=rc=lazy=0;
                memset(sum,0,sizeof(sum));
            }
            SegmentTree operator + (const SegmentTree &another)
            {	
                SegmentTree tmp;
                for(int i=1;i<=k;i++)
                {
                    for(int j=1;j<=k;j++)
                    {
                        tmp.sum[i][j]=sum[i][j]+another.sum[i][j];
                    }
                }
                tmp.sum[rc][another.lc]++;
                tmp.lc=lc;
                tmp.rc=another.rc;
                return tmp;
            }
        }tree[800010];
        int lson(int x)
        {
            return x*2;
        }
        int rson(int x)
        {
            return x*2+1;
        }
        void pushup(int rt)
        {
            tree[rt]=tree[lson(rt)]+tree[rson(rt)];
        }
        void build(int rt,int l,int r)
        {
            if(l==r)
            {
                tree[rt].lc=tree[rt].rc=s[l]-'a'+1;
                return;
            }
            int mid=(l+r)/2;
            build(lson(rt),l,mid);
            build(rson(rt),mid+1,r);
            pushup(rt);
        }
        int move_right(int x,int d)
        {
            return (x+d-1)%k+1;
        }
        void pushlazy(int rt,int lazy)
        {
            tree[rt].lc=move_right(tree[rt].lc,lazy);
            tree[rt].rc=move_right(tree[rt].rc,lazy);
            for(int i=1;i<=k;i++)
            {
                for(int j=1;j<=k;j++)
                {
                    tmp[move_right(i,lazy)][move_right(j,lazy)]=tree[rt].sum[i][j];
                }
            }
            for(int i=1;i<=k;i++)
            {
                for(int j=1;j<=k;j++)
                {
                    tree[rt].sum[i][j]=tmp[i][j];
                }
            }
            tree[rt].lazy=(tree[rt].lazy+lazy)%k;
        }
        void pushdown(int rt)
        {
            if(tree[rt].lazy!=0)
            {
                pushlazy(lson(rt),tree[rt].lazy);
                pushlazy(rson(rt),tree[rt].lazy);
                tree[rt].lazy=0;
            }
        }
        void update(int rt,int l,int r,int x,int y,int val)
        {
            if(x<=l&&r<=y)
            {
                pushlazy(rt,val);
                return;
            }
            pushdown(rt);
            int mid=(l+r)/2;
            if(x<=mid)
            {
                update(lson(rt),l,mid,x,y,val);
            }
            if(y>mid)
            {
                update(rson(rt),mid+1,r,x,y,val);
            }
            pushup(rt);
        }
        SegmentTree query(int rt,int l,int r,int x,int y)
        {
            if(x<=l&&r<=y)
            {
                return tree[rt];
            }
            pushdown(rt);
            int mid=(l+r)/2;
            if(y<=mid)
            {
                return query(lson(rt),l,mid,x,y);
            }
            if(x>mid)
            {
                return query(rson(rt),mid+1,r,x,y);
            } 
            return query(lson(rt),l,mid,x,y)+query(rson(rt),mid+1,r,x,y);
        }
        int ask(int l,int r,int n)
        {
            int sum=1;
            SegmentTree tmp=query(1,1,n,l,r);
            for(int i=1;i<=k;i++)
            {
                id[t[i]-'a'+1]=i;
            }
            for(int i=1;i<=k;i++)
            {
                for(int j=1;j<=k;j++)
                {
                    sum+=(id[i]>=id[j])*tmp.sum[i][j];
                }
            }
            return sum;
        }
    }T;
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("d.in","r",stdin);
        freopen("d.out","w",stdout);
    #endif
        int n,m,pd,l,r,c,i;
        cin>>n>>m>>k>>(s+1);
        T.build(1,1,n);
        for(i=1;i<=m;i++)
        {
            cin>>pd>>l>>r;
            if(pd==1)
            {
                cin>>c;
                T.update(1,1,n,l,r,c);
            }
            else
            {
                cin>>(t+1);
                cout<<T.ask(l,r,n)<<endl;
            }
        }
        return 0;
    }
    

总结

  • \(T2\)
    • 读假题了,把 至多 读成 恰好 了。
    • 又没想到转化到操作序列上排序,一直在想对某个单体怎么处理。
  • \(T3\) 理解错了有根树的含义,以为复制后的树中仍需满足原上下级关系,挂了 \(10pts\)
  • \(T4\) 赛时降智了,导致不会写判断一个数列是否是另一个数列的子序列。

后记

  • \(T1\) 的提示不知道有什么用。

  • \(T3\) 中途补充了大样例。

posted @ 2024-11-21 20:22  hzoi_Shadow  阅读(71)  评论(14编辑  收藏  举报
扩大
缩小