首页 > 其他分享 >B1024 题解

B1024 题解

时间:2023-10-25 15:22:54浏览次数:39  
标签:return int 题解 lv B1024 ans neq mod

本着 10 月杂题题解只记重量级的原则,再加上这个系列好久没更新了,搞一发。

原题链接

发挥还可以的一场,至少比 csp-s 发挥的好。

  • T1 智慧概率题,考场差点出来,30 pts。
  • T2 简单计数题,之前几场都卡 T2,终于出来一次,100 pts。
  • T3 简单数据结构题,打的 30 pts 暴力,但是有 50 pts。
  • T4 智慧网络流建模题,随便打了个 30 pts 暴力。

T1 赢钱

设 \(f(i)\) 表示当前有 \(i\) 元钱,得到 \(b\) 元钱的概率。

那么有:

\(\begin{cases} f(i)=Pf(2i) \ \ \ \ 2i\leq b \\ f(i)=(1-P)f(2i-b)+P \ \ \ \ 2i > b \end{cases}\)

然后你会发现转移可能形成了一个环。

比如转移顺序可能是这样的: \(f(a) \leftarrow f(b) \leftarrow f(c) \leftarrow f(a)\)。

设 \(f(a)=x\),带进去算,那么最后会得到 \(f(a)=x=kx+b\),然后就可以解得 \(x\)。

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e6+5,mod=998244353;
int a,b,P,Q,f,cnt,p[N],vis[N];//p[i]:第 i 次转移是哪种情况
int qpow(int a,int b) {int r=1;for(;b;b>>=1,a=a*a%mod) if(b&1) r=r*a%mod;return r;}
int inv(int a) {return qpow(a,mod-2);}

signed main()
{
    cin>>a>>b>>P;
    P=P*inv(1e6)%mod,Q=(1-P+mod)%mod;
    while(!vis[a]) vis[a]=++cnt,p[cnt]=(a*2>=b),a=a*2%b;
    int k=1,b=0;
    for(int i=cnt;i>=vis[a];i--)
    {
        if(p[i]) k=k*Q%mod,b=(b*Q+P)%mod;
        else k=k*P%mod,b=b*P%mod;
    }
    f=b*inv(1-k+mod)%mod;
    for(int i=vis[a]-1;i;i--)
    {
        if(p[i]) f=(f*Q+P)%mod;
        else f=f*P%mod;
    }
    cout<<f;
}

T2 排列

考虑把每个位置看为一个点,一个限制看为一条无向边。

然后得到一张图。

显然每个点度数不能大于 \(2\)。

图中连通块之间的限制基本是独立的。

假设当前连通块大小为 \(sz\)。

若当前连通块是个环,那么这些位置就有两种填法,会限制 \(sz\) 个位置。

若是一个链,还分两种情况。

若链的大小 \(>2\),那么两种填法至少有一个位置不相同,所以是两种,会限制 \(sz-1\) 个位置。

若链的大小 \(<2\),不能直接是两种填法,可能重复,会限制 \(1\) 个位置。

设总共有 \(k\) 条链的大小 \(<2\),有 \(p\) 个位置没有限制。

枚举有多少大小 \(<2\) 的链,限制了两个位置,容斥即可,那么对于这 \(k\) 条链,方案为:

\(\sum\limits_{0\leq i \leq k} (-1)^i \binom{k}{i}2^{k-i}(p-i)!\)

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=5e5+5,p=998244353;
int n,m,k,sum,ans=1,sz,_sz,lian;
int vis[N],pw[N]={1},fac[N]={1},inv[N];
vector<int> G[N];
map<pair<int,int>,bool> mp;//判重边
int qpow(int a,int b) {int r=1;for(;b;b>>=1,a=a*a%p) if(b&1) r=r*a%p;return r;}
int C(int n,int m) {if(n<m) return 0;return fac[n]*inv[m]%p*inv[n-m]%p;}

void dfs(int x)
{
    vis[x]=1;_sz++;
    if(G[x].size()<2) lian=1;
    for(int y:G[x]) if(!vis[y]) dfs(y);
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;sz=n;
    for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%p;
    for(int i=1;i<=n;i++) pw[i]=pw[i-1]*2%p;
    inv[n]=qpow(fac[n],p-2);
    for(int i=n-1;~i;i--) inv[i]=inv[i+1]*(i+1)%p;
    for(int i=1;i<=m;i++)
    {
        int x,y;cin>>x>>y;
        if(x>y) swap(x,y);
        if(mp.count({x,y})) continue;
        G[x].push_back(y),G[y].push_back(x);
        mp[{x,y}]=1;
    }
    for(int i=1;i<=n;i++) if(G[i].size()>2) {cout<<0;return 0;}
    for(int i=1;i<=n;i++) if(G[i].size()&&i==*G[i].begin()) sz--;//自环
    for(int i=1;i<=n;i++)
        if(!vis[i])
        {
            _sz=lian=0;
            dfs(i);
            if(_sz==1) continue;
            if(_sz==2) sz--,k++;
            else ans=ans*2%p,sz-=_sz-lian;
        }
    for(int i=0;i<=k&&i<=sz;i++) sum=(sum+((i&1)?-1:1)*pw[k-i]*C(k,i)%p*fac[sz-i]%p+p)%p;
    cout<<ans*sum%p;
}

T3 箱子

显然颜色相同的段之间独立。

一个很显然的结论,每次选尽可能大的区间进行操作。

那么对于一个点 \(i\),其作为左端点的次数为 \(\max(a_i-a_{i-1},0)\),作为右端点的次数为 \(\max(a_i-a_{i+1},0)\)。

如果 \(c_i \neq c_{j}\) 或者 \(i\) 作为某次询问的边界,那么在上面式子里对应 \(a_{j}=0\)。

考虑对于两种贡献用线段树分别维护,第一种单点修就用那个公式直接搞就完了,然后维护区间和。

然后另一棵线段树维护某些位置作为颜色段边界的贡献(草,这玩意好像可以用 BIT)。

对于一段颜色覆盖,可以用 ODT,个人感觉 ODT 巨难写,巨难调,巨容易错,一开始我就写错了近 10 个地方,调了 2.5 h。

而好多大神都没用 ODT。

复杂度 \(O(n\log n)\)。

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=2e5+5,inf=2e9;
int n,q,ans,c[N],w[N],a[N],b[N],isR[N];//isR:位置 i 是不是右端点
struct node{
    int l,r,col;
    bool operator<(const node&x)const{
        if(l==x.l) return r>x.r;
        return l<x.l;
    }
};
set<node> s;

inline int rd()
{
    int x=0;char c=getchar();
    for(;!isdigit(c);c=getchar());
    for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^48);
    return x;
}

#define lc (k<<1)
#define rc (k<<1|1)
#define mid (l+r>>1)
int sum[N<<2],sum2[N<<2];
void build(int k=1,int l=1,int r=n)
{
    if(l==r) {sum[k]=(max(a[l]-a[l-1],0ll)+max(a[l]-a[l+1],0ll))*w[l];return;}
    build(lc,l,mid),build(rc,mid+1,r);
    sum[k]=sum[lc]+sum[rc];
}
void upd(int x,int k=1,int l=1,int r=n)
{
    if(x<0||x>n) return;
    if(l==r) {sum[k]=(max(a[l]-a[l-1],0ll)+max(a[l]-a[l+1],0ll))*w[l];return;}
    x<=mid?upd(x,lc,l,mid):upd(x,rc,mid+1,r);
    sum[k]=sum[lc]+sum[rc];
}
void upd2(int x,int v,int k=1,int l=1,int r=n)
{
    if(x<0||x>n) return;
    if(l==r) {sum2[k]+=v;return;}
    x<=mid?upd2(x,v,lc,l,mid):upd2(x,v,rc,mid+1,r);
    sum2[k]=sum2[lc]+sum2[rc];
}

int qsum(int x,int y,int k=1,int l=1,int r=n)
{
    if(l>=x&&r<=y) return sum[k]+sum2[k];
    int res=0;
    if(x<=mid) res+=qsum(x,y,lc,l,mid);
    if(mid<y) res+=qsum(x,y,rc,mid+1,r);
    return res;
}
void Upd2(int i,int v) {upd2(i,v*min(a[i],a[i+1])*w[i]),upd2(i+1,v*min(a[i],a[i+1])*w[i+1]);}

signed main()
{
    n=rd(),q=rd();
    for(int i=1;i<=n;i++) a[i]=rd(),c[i]=rd(),w[i]=rd();
    build();
    s.insert({-inf,-inf,0}),s.insert({inf,inf,0});
    for(int i=1;i<=n;i++)
    {
        int j=i;
        while(j<n&&c[j+1]==c[i]) j++;
        if(j!=n) Upd2(j,1);
        isR[j]=1;
        s.insert({i,j,c[i]});
        i=j;
    }
    while(q--)
    {
        int op=rd();
        if(op==1)
        {
            int x=rd();
            if(isR[x]) Upd2(x,-1);
            if(isR[x-1]) Upd2(x-1,-1);
            a[x]=rd(),w[x]=rd();
            //注意这里会影响三个位置的值
            upd(x),upd(x-1),upd(x+1);
            if(isR[x]) Upd2(x,1);
            if(isR[x-1]) Upd2(x-1,1);
        }
        else if(op==2)
        {
            int l=rd(),r=rd(),v=rd();
            auto it=s.lower_bound({l,r,v});
            for(;it->l<=r;++it,s.erase(prev(it)))//合并左端点在 [l,r] 内的区间
            {
                auto [_l,_r,col]=*it;
                if(_r<=r) isR[_r]=0,Upd2(_r,-1);
                else
                {
                    if(v==col) isR[_r]=0,Upd2(_r,-1),r=_r;//颜色相同扩大右端点
                    else s.insert({r+1,_r,col});
                }
            }
            it=s.lower_bound({l,r,v});--it;
            auto [_l,_r,col]=*it;
            //合并右端点在 [l,r] 的区间
            //注意一定要先考虑某个超大区间包含了 [l,r] 的情况
            if(col==v) s.erase(it),isR[_r]=0,Upd2(_r,-1),l=_l,r=max(r,_r);
            else if(it->r>=l)
            {
                s.erase(it);
                if(_l<=l-1) s.insert({_l,l-1,col}),isR[l-1]=1,Upd2(l-1,1);
                if(_r<=r) isR[_r]=0,Upd2(_r,-1);
                else s.insert({r+1,_r,col});
            }
            it=s.lower_bound({l,r,v});
            //有可能有 [r+1,_r] 这种和当前颜色相同的区间,而前面合并不到
            if(it->col==v) r=it->r,Upd2(r,-1),s.erase(it);
            s.insert({l,r,v});
            isR[r]=1,Upd2(r,1);
        }
        else if(op==3)
        {
            int l=rd(),r=rd(),ans=qsum(l,r);
            if(!isR[r]) ans+=min(a[r],a[r+1])*w[r];
            if(!isR[l-1]) ans+=min(a[l],a[l-1])*w[l];
            printf("%lld\n",ans);
        }
    }
}

T4 扌非 歹刂

原 agc_038_f,有一点不同。

对于一个位置,如果选择移动,那么整个置换环都会移动。

记 \(x(i)\) 表示 \(A\) 中 \(i\) 所在的环是否移动,\(y(i)\) 表示 \(B\) 中 \(i\) 所在的环是否移动。

对于每一位,分类讨论。

  1. \(A_i=B_i=i\),怎么整都没有用,直接令 \(ans+1\)。
  2. \(A_i\neq i,B_i=i\),当且仅当移动 \(x(i)\) 答案不加 1。
  3. \(A_i=i,B_i\neq i\),类似情况 2。
  4. \(A_i=B_i\neq i\),当且仅当 \(x(i) \operatorname{xor} y(i)=1\) 答案不加 1。
  5. \(A_i\neq B_i,A_i \neq i,B_i \neq i\),当且仅当 \(x(i) \operatorname{or} y(i)=1\) 答案不加 1。

注意每个置换环有两种情况,旋转或不旋转,容易想到建立网络流模型,转化为最小割问题。

令 \(a\) 表示 \(A\) 中环的结点,\(b\) 表示 \(B\) 中环的结点,\((u,v,w)\) 表示 \(u\) 向 \(v\) 连了边权为 \(w\) 的边。

若 \(a\) 分入 \(S\) 集合表示旋转,\(b\) 分入 \(T\) 集合表示不转。

  • 割掉 \(S \rightarrow a\),代价为情况 2 的个数。
  • 割掉 \(b \rightarrow T\),代价为情况 3 的个数。
  • 对于情况 4,连边 \((a,b,1),(b,a,1)\),如果割掉,表示 \(a,b\) 在不同集合,那么表示要么都旋,要么都不旋,代价为 1。
  • 对于情况 5,连边 \((b,a,1)\),如果割掉,表示 \(a\) 分在集合 \(T\),\(b\) 分在集合 \(S\),是都不选的情况,代价为 1。
#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int n,ans,S,T=1;
int tot=1,pre[N*2],cur[N*2],lv[N*2];
int cnt1=1,cnt2,p[N],q[N],hp[N],hq[N],wp[N],wq[N];
struct edge{int to,nxt,w;} e[N*8];

void add(int u,int v,int w)
{
    e[++tot]={v,pre[u],w};pre[u]=tot;
    e[++tot]={u,pre[v],0};pre[v]=tot;
}

int bfs()
{
    memset(lv,0,sizeof(lv));
    memcpy(cur,pre,sizeof(cur));
    queue<int> q;q.push(S);lv[S]=1;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=pre[u];i;i=e[i].nxt)
        {
            int v=e[i].to,w=e[i].w;
            if(w>0&&!lv[v]) lv[v]=lv[u]+1,q.push(v);
        }
    }
    return lv[T];
}

int dfs(int u=S,int flow=1e9)
{
    if(u==T) return flow;
    int lev=flow;
    for(int i=cur[u];i&&lev;i=e[i].nxt)
    {
        cur[u]=i;
        int v=e[i].to,w=e[i].w;
        if(w>0&&lv[v]==lv[u]+1)
        {
            int out=dfs(v,min(lev,w));
            lev-=out,e[i].w-=out,e[i^1].w+=out;
        }
    }
    return flow-lev;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>p[i];
    for(int i=1;i<=n;i++) cin>>q[i];
    for(int i=1;i<=n;i++) if(!hp[i]) {hp[i]=++cnt1;for(int j=p[i];j!=i;j=p[j]) hp[j]=cnt1;}cnt2=cnt1;
    for(int i=1;i<=n;i++) if(!hq[i]) {hq[i]=++cnt2;for(int j=q[i];j!=i;j=q[j]) hq[j]=cnt2;}
    for(int i=1;i<=n;i++)
    {
        if(p[i]==q[i]&&p[i]==i) ans++;
        if(p[i]==i&&q[i]!=i) wq[hq[i]]++;
        if(p[i]!=i&&q[i]==i) wp[hp[i]]++;
        if(p[i]!=i&&q[i]!=i&&p[i]!=q[i]) add(hq[i],hp[i],1);
        if(p[i]!=i&&q[i]!=i&&p[i]==q[i]) add(hp[i],hq[i],1),add(hq[i],hp[i],1);
    }
    for(int i=2;i<=cnt1;i++) if(wp[i]) add(S,i,wp[i]);
    for(int i=cnt1+1;i<=cnt2;i++) if(wq[i]) add(i,T,wq[i]);
    while(bfs()) ans+=dfs();
    cout<<ans<<'\n';
}

标签:return,int,题解,lv,B1024,ans,neq,mod
From: https://www.cnblogs.com/spider-oyster/p/17787289.html

相关文章

  • CF1572F Stations 题解-Segment Tree Beats
    20231025CF1572FStations题解-SegmentTreeBeats吉司机线段树好题!!!CF3400。传送门Statement有\(n\)个广播站,第\(i\)个广播站高度为\(h_i\),范围为\(w_i\)。初始\(h_i=0,w_i=i\)。广播站\(i\)能向广播站\(j\)传递消息,当且仅当\(i\lej\lew_i\),且\(h_i>\max\lim......
  • 恨7不成妻题解
    恨7不成妻题解分析数位\(DP\)考虑题目中的两个条件,每一位不等于\(7\)直接枚举时把\(7\)排除,其他两种情况直接放在状态里。因为题目要求平方和,我们考虑每次加上一位(设加入的是第\(i\)位)时会发生什么设原平方和为\[\sum_{k=1}^ta_k^2\]加入一位\(p\)之后每个数......
  • P9771 HUSTFC 2023 排列排序问题 题解
    Question给出一个\(N\)个元素的排序\(a\),我们可以对排列进行一些操作将这个排列切割成若干个序列将其中一些序列翻转将这些序列连接起来得到一个新的排列需要让最后的排列有序Solution这个题的描述有点小问题理解应该是切一次,然后再反转合并,不可能会先合并再切再反转......
  • P9744 「KDOI-06-S」消除序列 题解
    @目录DesciptionSolutionCodeDesciption给定一个长度为\(i\)的序列\(v_1,v_2,\dots,v_n\),初始时所有元素的值都为\(1\)。对于下标\(i\)有\(3\)种操作:将\(v_1,v_2,\dots,v_i\)的值变为\(0\),费用是\(a_i\)。将\(v_i\)的值变为\(0\),费用是\(b_i\)。将\(v_i\)......
  • Meta Hacker Cup 2023 Round 1 题解
    ProblemA:HereComesSantaClaus给一个数列,要求分成若干组,要求每组至少2个数,使得所有组中位数的最大值与最小值之差尽量大,求这个值。#include<bits/stdc++.h>usingnamespacestd;#defineFor(i,n)for(inti=1;i<=n;i++)#defineFork(i,k,n)for(inti=k;i<=n;i++)#define......
  • pytest运行警告问题解决:DeprecationWarning: pkg_resources is deprecated as an API
    前言最近在运行pytest的时候,经常出现这个警告DeprecationWarning:pkg_resourcesisdeprecatedasanAPISeehttps://setuptools.pypa.io/en/latest/pkg_resources.htmlfrompkg_resourcesimportiter_entry_points从警告上看是方法被弃用,肯定是因为新版弃用了旧版的语法。遇......
  • szfpga Lattice高速下载器HW-USBN-2B 常见问题解答
      .产品特点     1).支持windows7,Windows10操作系统,两个操作系统非常稳定不断线。  2).支持JTAG模式,速度快,最高30Mb/s,调试serdescore,不会像hw-usbn-2a出现错误。如这种错误Error:failedtosetcablepor(cable:USBport:EzUSB-0error:-1)  3). ......
  • CF1854E Games Bundles 题解
    乱搞题设个\(dp[i]\)表示和为\(i\)的子序列个数,那么转移是容易的,\(dp[j]+=dp[j-i]\),然后就判下\(dp[60]+dp[60-i]\)是否大于\(m\),发现这样子搞对于比较大的数可能达不到\(m\)的限制,因为这样子转移,默认的是一个数只选一次,但是我们可以重复选,这启发我们需要设定一个值......
  • 题解 CF903G【Yet Another Maxflow Problem】
    加边\(A_n\stackrel{0}{\to}A_{n+1}\),\(B_0\stackrel{0}{\to}B_1\)。称形如\(A_i\toA_{i+1}\)的边为左部边,形如\(B_j\toB_{j+1}\)的边为右部边,形如\(A_i\toB_j\)的边为中间边。根据最大流最小割定理,将最大流问题转化为最小割问题求解。显然,至少存在一组最小割,包含恰好......
  • CF1777D题解
    分析首先看到那个\(10^{100}\)再加上样例,我们就能意识到在不是特别多的操作次数后这颗树上的值就会全变成\(0\)。因为没有子节点在一次操作后显然就会变成\(0\),然后第一次叶节点就会变成\(0\),然后下一次子节点中只有叶节点的就会变成\(0\),以此类推,理论上最多操作\(n\)......