2020牛客暑期多校训练营(第一场)

根据难度排序。
个人总结向。
如果有什么讲的不清楚的欢迎留言私信交流~

F. Infinite String Comparision(字符串+结论)

题意: 给出两个无限循环串的循环节,比较两个串的大小。

思路:
假设一个字符串 S 有循环节(不需要是完整循环节) p 和 q ,并且满足 ,那么 也是一个循环节。

证明点这里

题解中说到只要复制到长度为(n+m)即可,也就是说可以记住这个结论。

代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=1e6+5;
const int N=1e3+5;
const int mod=1e9+7;

int main()
{
    string a,b;
    while(cin>>a>>b){
        string s1=a+b,s2=b+a;
        if(s1<s2) cout<<"<"<<endl;
        else if(s1==s2) cout<<"="<<endl;
        else cout<<">"<<endl;
    }
    return 0;
}

J. Easy Integration(欧拉积分)

题意: 输出分数

思路:

欧拉积分:
伽玛函数:
贝塔函数:

过程:

代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=2e6+5;
const int N=1e3+5;
const int mod=998244353;

ll f[manx],n;
ll qp(ll a, ll b){
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans%mod;
}
ll inv(ll x){
    return qp(x,mod-2);
}
int main()
{
    io; 
    f[1]=1;
    for(int i=2;i<=2000005;i++) f[i]=f[i-1]*i%mod;
    while(cin>>n){
        ll ans=f[n]*f[n]%mod*inv(f[2*n+1])%mod;
        cout<<ans<<endl;
    }
    return 0;
}

H. Minimum-cost Flow(最小费用最大流)

题意: 给定网络的边和费用,每次询问给定所有边的容量u和要求的流量v,问把一单位流量从1 流向 n 最少所需要的费用,如果不能满流输出NaN。

思路:
很明显每条边的容量都是相同的,所以我们可以初始化为1,这样做的好处是 就是当前询问的最大流,在增广路径的时候用 map 记录每一条路径的流量与花费(map会自动排序)。
如果 的话,则输出NaN 。
否则的话,我们不断地加入每一条路径,并且累加费用,直到流量不小于 v 时跳出循环,最后总的费用输出时分子分母同时除以分子分母的GCD即可。

代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=2e3+5;
const int N=1e3+5;
const int mod=1e9+7;

bool vis[manx];
ll n,m,s,t,x,y,z,f,maxflow,mincost,cnt;
ll dis[manx],pre[manx],last[manx],flow[manx],head[manx];
map<ll,ll>mps;
struct node{
    ll v,next,flow,dis;
}a[manx];

inline void add(ll u,ll v,ll flow,ll dis){
    a[++cnt].next=head[u],a[cnt].v=v,a[cnt].flow=flow,a[cnt].dis=dis,head[u]=cnt;
}

bool spfa(ll s,ll t)
{
    for(int i=1;i<=n;i++) vis[i]=0,dis[i]=flow[i]=inf;
    queue<ll>q;
    q.push(s); vis[s]=1; dis[s]=0; pre[t]=-1;
    while(!q.empty()){
        ll now=q.front();
        q.pop();
        vis[now]=0;
        for(int i=head[now];i!=-1;i=a[i].next){
            if(a[i].flow>0&&dis[a[i].v]>dis[now]+a[i].dis){
                dis[a[i].v]=dis[now]+a[i].dis;
                pre[a[i].v]=now;
                last[a[i].v]=i;
                flow[a[i].v]=min(flow[now],a[i].flow);
                if(!vis[a[i].v]){
                    vis[a[i].v]=1;
                    q.push(a[i].v);
                }
            }
        }
    }
    return pre[t]!=-1;
}
inline void donic()  
{
    while(spfa(s,t)){
        ll now=t;
        maxflow+=flow[t];
        mincost+=flow[t]*dis[t];
        mps[flow[t]*dis[t]]+=flow[t];
        while(now!=s){
            a[last[now]].flow-=flow[t];
            a[last[now]^1].flow+=flow[t];
            now=pre[now];
        }
    }
}
inline void init(){
    for(int i=1;i<=n;i++) head[i]=-1,last[i]=0;
    cnt=-1; maxflow=mincost=0; s=1,t=n;
    mps.clear();
}
int main()
{
    while(scanf("%lld%lld",&n,&m)!=EOF){
        init();
        for(int i=1;i<=m;i++){
            scanf("%lld%lld%lld",&x,&y,&f);
            add(x,y,1,f); add(y,x,0,-f);
        }
        donic();
        ll q; cin>>q;
        while(q--){
            ll u,v; scanf("%lld%lld",&u,&v);
            if(maxflow*u<v) printf("NaN\n");
            else{
                x=0,y=v;
                for(auto sb: mps){
                    if(v>sb.se*u) v-=sb.se*u,x+=sb.fi*u;
                    else {
                        x+=sb.fi/sb.se*v; break;
                    }
                }
                z=__gcd(x,y);
                printf("%lld/%lld\n",x/z,y/z);
            }
        }
    }
    return 0;
}

I. 1 or 2(带花树)

题意: 给出一张图和每个点的期望度数,要求你删掉一些边使得每个点的度数都等于期望度数,问是否可行。

思路:
在这之前只知道匈牙利算法,现在了解到了一般图最大匹配的解决算法。
以知道这个算法为前提,难点在于想到拆点拆边。(当时我就不知道)
对于每个点要求的度数 ,我们将点 i 拆成
对于每条边,我们将拆成,x,y,x与分别与 u 拆成的 个点连边,y点同理,最后再将连一条边(这样做是可以防止 u 或者 v 要求的度数为0的情况,此时直接在 x 与 y 之间连一条边就可以解决)
例如,题目中第三组样例:
3 2
1 1 2
1 3
2 3
第1个点拆成1个点,编号为1。
第2个点拆成1个点,编号为2。
第3个点拆成2个点,编号分别为3,4
第1条边拆成两个点,编号为5,6:(5,6)(5,1)(6,3)(6,4)连边。
第2条边拆成两个点,编号为7,8:(7,8)(7,2)(8,3)(8,4)连边。
注意在这题里是无向边。

代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=2e3+5;
const int N=1e3+5;
const int mod=1e9+7;

deque<ll>q;
bool g[N][N],inque[N],inblossom[N];
ll match[N],pre[N],base[N];
inline ll findancestor(ll u,ll v){  //找公共祖先
    bool inpath[N]={false};
    while(1){
        u=base[u];
        inpath[u]=true;
        if(match[u]==-1)break;
        u=pre[match[u]];
    }
    while(1){
        v=base[v];
        if(inpath[v])return v;
        v=pre[match[v]];
    }
}
inline void reset(ll u,ll anc){ //压缩花
    while(u!=anc)
    {
        ll v=match[u];
        inblossom[base[u]]=1;
        inblossom[base[v]]=1;
        v=pre[v];
        if(base[v]!=anc)pre[v]=match[u];
        u=v;
    }
}
inline void contract(ll u,ll v,ll n){
    ll anc=findancestor(u,v);
    memset(inblossom,0,sizeof(inblossom));
    reset(u,anc);
    reset(v,anc);
    if(base[u]!=anc)pre[u]=v;
    if(base[v]!=anc)pre[v]=u;
    for(int i=1;i<=n;i++)
        if(inblossom[base[i]]){
            base[i]=anc;
            if(!inque[i]) q.push_back(i),inque[i]=1;
        }
}
inline bool dfs(ll S,ll n)
{
    for(int i=0;i<=n;i++) pre[i]=-1,inque[i]=0,base[i]=i;
    q.clear(); q.push_back(S); inque[S]=1;
    while(!q.empty()){
        ll u=q.front(); q.pop_front();
        for(int v=1;v<=n;v++){
            if(g[u][v]&&base[v]!=base[u]&&match[u]!=v){
                if(v==S||(match[v]!=-1&&pre[match[v]]!=-1)) contract(u,v,n);
                else if(pre[v]==-1){
                    pre[v]=u;
                    if(match[v]!=-1) q.push_back(match[v]),inque[match[v]]=1;
                    else{
                        u=v;
                        while(u!=-1){
                            v=pre[u];
                            ll w=match[v];
                            match[u]=v;
                            match[v]=u;
                            u=w;
                        }
                        return true;
                    }
                }
            }
        }
    }
    return false;
}
inline int solve(ll n){  
    memset(match,-1,sizeof(match));
    ll ans=0;
    for(int i=1; i<=n; i++)
        if(match[i]==-1&&dfs(i,n))
            ans++;
    return ans;
}
//以上是带花树模板
//g[i][j]存放关系图:i,j是否有边,match[i]存放i所匹配的点
//建图开始初始化g
//最终匹配方案为match
//复杂度O(n^3)
//点是从1到n的
//最终带花树solve给出的是匹配对数
ll d[60];
ll x[210],y[210];
ll mps[55][210];
inline bool build(ll n,ll m)
{
    ll num=0;
    //拆点,给i点的第j个度一个标号,方便接下来的建图
    for(int i=1;i<=n;i++)
        for(int j=1;j<=d[i];j++)
            mps[i][j]=++num; 
    //拆边,把边被拆成的两个点分别与两头的每个度相连
    for(int i=1;i<=m;i++){ 
        for(int j=1;j<=d[x[i]];j++) g[mps[x[i]][j]][num+1]=g[num+1][mps[x[i]][j]]=1;
        for(int j=1;j<=d[y[i]];j++) g[mps[y[i]][j]][num+2]=g[num+2][mps[y[i]][j]]=1;
        g[num+1][num+2]=g[num+2][num+1]=1;
        num+=2;
    }
    if(solve(num)*2==num) return 1;
    return 0;
}
int main()
{
    ll n,m;
    while(scanf("%lld%lld",&n,&m)!=EOF){
        memset(g,0,sizeof(g));
        for(int i=1;i<=n;i++) scanf("%lld",&d[i]);
        for(int i=1;i<=m;i++) scanf("%lld%lld",&x[i],&y[i]);
        if(build(n,m)) puts("Yes");
        else puts("No");
    }
    return 0;
}
全部评论

相关推荐

昨天 18:54
门头沟学院 Java
点赞 评论 收藏
分享
已老实求offer😫:有点像徐坤(没有冒犯的意思哈)
点赞 评论 收藏
分享
1 1 评论
分享
牛客网
牛客企业服务