CF818div2 做题记录

E

错误思路:一开始直接拆开了 \(\mathbb{lcm}\) 导致分母上有一个 \(\gcd\),不好处理。

有:\(\gcd(a,b)=\gcd(a,a+b)=\gcd(a,n-c)\),我们先枚举 \(c\),而 \(\gcd(a,b)\) 一定是 \(n-c\) 的倍数,所以我们有:

\[\begin{aligned} \sum\limits_{a+b+c=n}\mathrm{lcm}(c,\gcd(a,b))=\sum\limits_{c}\sum\limits_{d|n-c}\mathrm{lcm(c,d)}\sum\limits_{a,b}[\gcd(a,b)=d][a+b=n] \end{aligned} \]

我们考虑后面那个式子,有:

\[\begin{aligned} \gcd(a,b)=d\Rightarrow\gcd(a,n-c)=d\Rightarrow \gcd(\frac{a}{d}\frac{n-c}{d})=1 \end{aligned} \]

而一定有 \(a<n-c,b=n-c-a\),所以合法 \((a,b)\) 的个数应为 \(\phi(\frac{n-c}{d})\)

等价于我们要求:

\[\begin{aligned} \sum\limits_{c}\sum\limits_{d|n-c}\mathrm{lcm}(c,d)\phi(\frac{n-c}{d})=\sum\limits_{d}\sum\limits_{c=1}^{\left\lfloor \frac{n}{d} \right\rfloor} \mathrm{lcm}(n-cd,d)\phi(c) \end{aligned} \]

int pr[N],ta,phi[N],n;
bool no[N];

inline void GetPrime(int n){
    no[1]=1;phi[1]=1;
    rep(i,2,n){
        if(!no[i]) pr[++ta]=i,phi[i]=i-1;
        for(int j=1;j<=ta&&1ll*pr[j]*i<=n;j++){
            no[pr[j]*i]=1;
            if(i%pr[j]==0){
                phi[i*pr[j]]=pr[j]*phi[i];
                break;
            }
            else phi[i*pr[j]]=phi[i]*phi[pr[j]];
        }
    }
}
inline int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}
inline int lcm(int a,int b){
    return a*b/gcd(a,b);
}

signed main(){
    read(n);GetPrime(100000);
    int ans=0;
    rep(d,1,n){
        rep(c,2,n/d){
            // if(c*d<=1) continue;
            ans=(ans+lcm(n-c*d,d)*phi[c]%mod)%mod;
            // printf("ans=%d c=%d d=%d\n",ans,c,d);
        }
    }
    printf("%lld\n",ans);
    return 0;
}

F

容易想到我们就在原图上做网络流,从源点向每个点连边,从每个点向汇点连边即可。

但是,标记为 \(0\) 的点与源汇点之间的边权难以界定,界定的不好可能会忽略一些边导致没有选上所有的策略。

考虑这其实代表着我们设计的网络流对每个边必须选这个限制做得不够好,于是考虑先做这个限制。

所以从源点向原图每条边连边,原图每条边向其端点连边,这样做需要解决的问题是不好判断是否合法。

一个非常妙的思路是我们可以把 \(a_i\) 的限制进行转化,转化成应该有多少条边指向 \(i\),设为 \(b_i\),简单讨论后有 \(b_i=\frac{a_i+deg_i}{2}\)

于是从每个点往 \(i\) 连容量为 \(b_i\) 的边,跑网络流即可。

如何判 NO,发现还是可能会出现有些边会忽略的现象,注意最一开始的做法是不能跑上下界网络流的,因为原图中点边流量下界无法设定,不过这个做法显然是可以用上下界网络流来做的但是这样会破坏掉二分图跑 dinic 的时间复杂度。

考虑我们先不建所有无限制点到汇点的边,跑一次 dinic,看是否每个点到汇点的边都满足,然后再加上,跑一次 dinic,看是否原点到每个原图中边的边都满足,这样做正确性是显然的,因为第二次 dinic 的所有增广路一定都是无限制点到汇点的连边造成的。

代码:

struct edge{
    int from,to,next,f;
    inline void Init(int fr_,int to_,int ne_,int f_){
        from=fr_;to=to_;next=ne_;f=f_;
    }
}li[N<<1];
int head[N],now[N],tail=1;
int n,m,s,t;
P e[N];
int S[N],a[N],d[N],D[N];
bool vis[N],po[N];
vi v;
queue<int> q;

inline void Add(int from,int to,int f){
    // printf("from=%d to=%d f=%d\n",from,to,f);
    li[++tail].Init(from,to,head[from],f);head[from]=tail;swap(from,to);
    if(from<=n&&to<=n){li[++tail].Init(from,to,head[from],1);head[from]=tail;}
    else{li[++tail].Init(from,to,head[from],0);head[from]=tail;}

}
inline bool bfs(){
    mset(d,0);d[s]=1;now[s]=head[s];q.push(s);
    while(q.size()){
        int top=q.front();q.pop();
        for(int x=head[top];x;x=li[x].next){
            int to=li[x].to,f=li[x].f;
            if(!f||d[to]) continue;
            d[to]=d[top]+1;now[to]=head[to];q.push(to);
        }
    }
    if(d[t]) return 1;else return 0;
}
inline int dinic(int k,int flow){
    if(k==t) return flow;
    int rest=flow,x;
    for(x=now[k];x&&rest;x=li[x].next){
        int to=li[x].to,f=li[x].f;
        if(d[to]!=d[k]+1||!f) continue;int re=dinic(to,min(rest,f));
        if(!re) d[to]=0;li[x].f-=re;li[x^1].f+=re;rest-=re;
    }
    now[k]=x;return flow-rest;
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);read(m);
    rep(i,1,n){read(S[i]);if(S[i]) v.pb(i);}
    rep(i,1,n) read(a[i]);for(int x:v) vis[x]=1;
    rep(i,1,m){
        int u,v;read(u);read(v);D[u]++;D[v]++;
        Add(i,u+m,1);Add(i,v+m,1);e[i]=mp(u,v);
    }
    int tot=n+m;s=++tot;t=++tot;
    rep(i,1,m) Add(s,i,1);
    bool op=1;
    rep(i,1,n){
        if(abs(a[i])%2!=D[i]%2&&vis[i]) op=0;
    }
    rep(i,1,n) a[i]=(a[i]+D[i])/2;
    rep(i,1,n) if(a[i]<0) op=0;
    // printf("op=%d\n",op);
    int sum=0;
    rep(i,m+1,m+n){
        if(vis[i-m]) Add(i,t,a[i-m]),sum+=a[i-m];
        // else Add(i,t,INF);
    }
    vc<P> Ans;Ans.clear();
    int ans=0;
    while(bfs()){
        // puts("Here");
        ans+=dinic(s,INF);
    }
    if(ans!=sum) op=0;
    rep(i,1,n)if(!vis[i]) Add(i+m,t,INF);
    while(bfs()){
        ans+=dinic(s,INF);
    }
    if(ans!=m) op=0;
    if(!op){puts("No");return 0;}
    else{
        rep(i,1,m){
            Next(i){
                int to=li[x].to,f=li[x].f;
                if(to==s) continue;
                if(f) continue;
                // printf("i=%d to=%d f=%d\n",i,to,f);
                if(to-m==e[i].fi) Ans.pb(mp(e[i].se,e[i].fi));
                else Ans.pb(mp(e[i].fi,e[i].se));
            }
        }
    }
    puts("Yes");
    for(P now:Ans){
        printf("%d %d\n",now.fi,now.se);
    }
    return 0;
}
posted @ 2023-07-13 21:30  NuclearReactor  阅读(3)  评论(0编辑  收藏  举报