• FR #1题解


    A.

      建图跑最小费用最大流。分类讨论每种情况如何连边,费用怎么定。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define maxv 105
    #define maxe 100500
    #define inf 1000000000
    using namespace std;
    int n,m,a[maxv],b[maxv],c[maxv],d[maxv],s,t,fin[maxv],fout[maxv],g[maxv],dis[maxv],nume=1,ans=0,pree[maxv],prev[maxv];
    bool vis[maxv];
    queue <int> q;
    struct edge
    {
        int v,f,c,nxt;
    }e[maxe];
    void addedge(int u,int v,int f,int c)
    {
        e[++nume].v=v;e[nume].nxt=g[u];
        e[nume].f=f;e[nume].c=c;g[u]=nume;
        e[++nume].v=u;e[nume].nxt=g[v];
        e[nume].f=0;e[nume].c=-c;g[v]=nume;
    }
    bool spfa()
    {
        for (int i=s;i<=t;i++) {vis[i]=false;dis[i]=inf;}
        dis[s]=0;q.push(s);
        while (!q.empty())
        {
            int head=q.front();q.pop();
            for (int i=g[head];i;i=e[i].nxt)
            {
                int v=e[i].v;
                if ((e[i].f) && (dis[v]>dis[head]+e[i].c))
                {
                    dis[v]=dis[head]+e[i].c;
                    pree[v]=i;prev[v]=head;
                    if (!vis[v]) {vis[v]=true;q.push(v);}
                }
            }
            vis[head]=false;
        }
        if (dis[t]==inf) return false;
        return true;
    }
    int dinic()
    {
        int u=t,dt=inf;
        while (u!=s)
        {
            dt=min(dt,e[pree[u]].f);
            u=prev[u];
        }
        u=t;
        while (u!=s)
        {
            e[pree[u]].f-=dt;
            e[pree[u]^1].f+=dt;
            u=prev[u];
        }
        return dis[t]*dt;
    }
    int main()
    {
        scanf("%d%d",&n,&m);s=0;t=n+1;
        for (int i=1;i<=m;i++)
        {
            scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
            fout[a[i]]+=d[i];fin[b[i]]+=d[i];
        }
        for (int i=1;i<=n;i++)
        {
            if (fin[i]>fout[i]) addedge(s,i,fin[i]-fout[i],0);
            else addedge(i,t,fout[i]-fin[i],0);
        }
        addedge(n,1,inf,0);
        for (int i=1;i<=m;i++)
        {
            if (d[i]>c[i])
            {
                addedge(a[i],b[i],inf,2);
                addedge(b[i],a[i],d[i]-c[i],0);
                addedge(b[i],a[i],c[i],1);
                ans+=d[i]-c[i];
            }
            else
            {
                addedge(a[i],b[i],c[i]-d[i],1);
                addedge(a[i],b[i],inf,2);
                addedge(b[i],a[i],d[i],1);
            }
        }
        while (spfa()) ans+=dinic();
        printf("%d
    ",ans);
        return 0;
    }

    B.

      奇妙的数学题。

        首先for i=2 -> n是肯定的。考虑如何在log^2n的时间内得到答案。

          有一个log^3n的做法是显然的。对于一个新的i,我们枚举它所有的质因数,加到一个数组里,那么这是我们的答案就是对于每个质因数的答案和上一个的答案取max(每一个质因数的答案:若2出现i次,则找到2出现次数大于i次的最小的那个n)。然后我们二分答案,通过[n/p]+[n/p^2]+[n/p^3]+....算出答案。

          考虑如何优化(神来之笔)。将n看成p进制的数akak-1ak-2....a1a0。然后考虑上面那个式子的答案就是Σai*(p^i-1)/(p-1),且使上面那个式子刚好>=cnt。怎么确定数组a呢?直接贪心就好了!预处理后面关于p的多项式,之后能取大就取大。这样一定是正确的(但是我不知道为什么)。。。。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxn 1000050
    #define mod 1000000007
    using namespace std;
    long long n,k,prime[maxn/5],tot=0,mn[maxn],ans=0,tab[maxn],kr=0,num[maxn];
    bool vis[maxn];
    void get_table()
    {
        mn[1]=1;
        for (long long i=2;i<=maxn-50;i++)
        {
            if (!vis[i]) {vis[i]=true;prime[++tot]=i;mn[i]=i;}
            for (long long j=1;j<=tot && i*prime[j]<=maxn-50;j++)
            {
                vis[i*prime[j]]=true;mn[i*prime[j]]=prime[j];
                if (!i%prime[j]) break;
            }
        }
    }
    long long ask(long long x,long long cnt)
    {
        if (x==-1) return 0;
        long long lim=1,ret=1,ans=0;tab[1]=1;
        while (tab[ret]<=cnt) tab[++ret]=tab[ret-1]*x+1LL;
        ret--;
        for (long long i=ret;i>=1;i--)
        {
            ans=ans*x+cnt/tab[i];
            cnt%=tab[i];
        }
        return ans*x;
    }
    int main()
    {
        scanf("%lld%lld",&n,&k);
        get_table();
        for (long long i=2;i<=n;i++)
        {
            long long x=i,ret1=-1,cnt=0,rr;
            while (x!=1)
            {
                if (mn[x]!=ret1) {ans=max(ans,ask(ret1,num[ret1]*k));ret1=mn[x];}
                num[mn[x]]++;
                rr=mn[x];x/=mn[x];
            }
            ans=max(ans,ask(ret1,num[rr]*k));cnt=1;
            kr=(kr+ans)%mod;
        }
        printf("%lld
    ",kr);
        return 0;
    }

    C.

      杜教筛神题。。。感觉先要学一学杜教筛的东西再来看。就没写啦。

  • 相关阅读:
    PUTTY与SecureCRT的比较
    java中volatile关键字的含义
    java中引用的原理
    Java陷阱之assert关键字
    脏读 幻读 不可重复读
    JAVA 的wait(), notify()与synchronized同步机制
    线程状态转换图
    并行、并发、同步和互斥
    B-树学习笔记
    平衡二叉树及其应用场景
  • 原文地址:https://www.cnblogs.com/ziliuziliu/p/6188505.html
Copyright © 2020-2023  润新知