BestCoder Round #74

A题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5635

官方题解:

如果a_i=xai=x, 那么可以推断出s_i=s_{i+1}=...=s_{i+x}si=si+1=...=si+x, 并且如果a_i \ne 0ai0, 那么a_{i+1}=a_i-1ai+1=ai1, 利用第二个条件判断无解, 利用第一个条件划分等价类. 假设有mm个等价类, 那么答案就是26\cdot 25^{m-1}2625m1

我的思考:

简单模拟题,还是容易考虑不全面,首先最长公共前缀的长度不能大于后面的最大长度,然后a[i]!=0时a[i+1]必然等于a[i]

#include<cstdio>
#include<cstring>
using namespace std;
#define LL __int64
int T,i,n,a[100005];
LL ans;
const int mod=1e9+7;
int main()
{
    scanf("%d",&T);
    a[1]=0;
    while(T--)
    {
        scanf("%d",&n);
        ans=26;
        for(i=2;i<=n;i++) scanf("%d",&a[i]);
        for(i=2;i<=n;i++)
        {
            if(n-i+1<a[i]||(a[i-1]!=0&&a[i]!=a[i-1]-1))
            {
                ans=0;
                break;
            }
            if(a[i]==0)
                ans=(ans*25)%mod;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

B题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5636

官方题解

你可以选择分类讨论, 但是估计可能会写漏一些地方. 只要抽出新增边的端点作为关键点, 建立一个新图, 然后跑一遍floyd就好了. 复杂度大概O(6^2 \cdot m)O(62m)

我的思考:

由于只是新加三个点,所以对于每一个询问,可以暴力枚举所有可能的捷径组合,之前在比赛的时候只考虑到经过一次捷径,没有考虑到可以多条捷径组合 too young to naive

#include<cstdio>
#include<cstring>
#include<cmath>
#define LL __int64
using namespace std;
const int MOD=1e9+7;
int t,f[6],a[6];
LL ans,cnt;
LL min(LL x,LL y)
{
    return x>y?y:x;
}
void dfs(int s,int dist)
{
    ans=min(ans,abs(s-t)+dist);
    for(int i=0;i<6;i++)
    {
        if(f[i])
        {
            f[i]=f[i^1]=0;
            if(dist+abs(s-a[i])+1<ans)
            dfs(a[i^1],dist+abs(s-a[i])+1);
            f[i]=f[i^1]=1;
        }
    }
}
int main()
{
    int T,i,n,m,s;
    int cnt;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(i=0;i<6;i++)
            scanf("%d",&a[i]),f[i]=1;
        cnt=0;
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&s,&t);
            ans=MOD;
            dfs(s,0);
            cnt=(cnt+i*ans)%MOD;
        }
        printf("%d\n",cnt);
    }
    return 0;
}


C题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5637

官方题解:

注意到答案实际上只和s \oplus tst有关, bfs预处理下从0到xx的最短步数, 然后查询O(1)O(1)回答即可.
我的思考:

正如官方题解所说 s^a^b^c……=t 等价于 a^b^c……=s^t

所以原题目就转换乘按题目所给条件从0转化成s^t最短需要几步,预处理情况,之后的询问只要查询数组就可以了

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int mod=1e9+7;
const int maxn=1e5+4e4;
int T,n,m,ans;
int a[20],f[maxn],x,y;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++) scanf("%d",&a[i]);
        memset(f,1,sizeof(f));
        queue<int>q;
        q.push(0);
        f[0]=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=0;i<n;i++)
            {
                if(f[u^a[i]]>f[u]+1)
                {
                    f[u^a[i]]=f[u]+1;
                    q.push(u^a[i]);
                }
            }
            for(int i=0;i<17;i++)
            {
                if(f[u^(1<<i)]>f[u]+1)
                {
                    f[u^(1<<i)]=f[u]+1;
                    q.push(u^(1<<i));
                }
            }
        }
        int res=0;
        for(int k=1;k<=m;k++)
        {
            scanf("%d%d",&x,&y);
            (res+=k*f[x^y])%=mod;
        }
        printf("%d\n",res);
    }
    return 0;
}

D题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5638

官方题解:

参考下普通的用堆维护求字典序最小拓扑序, 用某种数据结构维护入度小于等于kk的所有点, 每次找出编号最小的, 并相应的减少kk即可.

这个数据结构可以用线段树, 建立一个线段树每个节点[l,r][l,r]维护编号从llrr的所有节点的最小入度, 查询的时候只需要在线段树上二分, 找到最小的xx满足入度小于等于kk.

复杂度O((n+m)\log n)O((n+m)logn)

我的思考:

可以用优先队列来做,对于拓扑序列,只要入度为0,那么就可以作为当前的根节点加入序列当中,所以我们可以先求出所有点的入度,按编号升序加入优先队列,我们知道要想使得字典序最小,我们要不惜一切代价使得当前的每一个位置,能取到尽可能小的点,而完全不用顾忌我们要消耗多少的机会来达到这一目的

#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e5+5;
const int maxm=2e5+5;
int in[maxn];
vector<int>a[maxn];
bool vis[maxn];
typedef pair<int,int> p;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m,k;
        scanf("%d%d%d",&n,&m,&k);
        memset(in,0,sizeof(in));
        for(int i=0;i<=n;i++)a[i].clear();
        while(m--)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            a[x].push_back(y);
            in[y]++;
        }
        priority_queue<int,vector<int>,greater<int> >q;
        for(int i=1;i<=n;i++){
            q.push(i);
        }
        ll ans=0,cnt=1;
        memset(vis,false,sizeof(vis));
        while(!q.empty())
        {
            int temp=q.top();
            q.pop();
            if(k<in[temp])continue;
            if(vis[temp])continue;
            k-=in[temp];
            vis[temp]=true;
            ans+=((temp*cnt)%mod);ans%=mod;
            cnt++;
            int tx=temp;
            int num=a[tx].size();
            for(int i=0;i<num;i++){
                in[a[tx][i]]--;
                if(!vis[a[tx][i]])
                    q.push(a[tx][i]);
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}
E题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5639

官方题解:

方法一:

考虑删掉的边的形态, 就是我们经常见到的环套树这种结构, 参考平时这种图给出的方法, 如果一个图的每个点的出边只有一条, 那么一定会构成环套树这种结构. 于是问题可以转化成, 给无向图的每条边定向, 使得出度最大点的出度最小 (每个点的出度大小对应了删的次数).

显然, 这个东西使可以二分的, 不妨设二分值为xx. 考虑混合图的欧拉回路的做法, 利用网络流判合法. 先给每条无向边随便定向, 对于出度大于xx的, 从源点连一条流量为deg-xdegx的边, 对于出度小于xx的, 从这个点连一条流量为x-degxdeg的边到汇点. 对于原来图中的边, 流量为1加到网络流图中. 只要满流就是合法.

方法二:

类似方法一, 要求的无非是每条边的归属问题, 对于每条边(a,b)(a,b), 它可以属于aa或者bb, 那么新建一个节点表示这条边并和a,ba,b都相邻, 这样就得到了一个二分图. 左边是原图中的节点, 右边是原图中的边. 二分每个左边每个节点的容量kk, 如果右边的点能够完全匹配, 那么这个kk就是可行的, 找到最小的kk即可. 转化成二分图多重匹配问题.

方法三:

事实上这题存在O(nm)O(nm)的做法, 只要在方法二的基础上继续改进就好了, 二分是没有必要的. 注意到每次增广的时候, 增光路中只有端点的容量会变化, 增广路中间的点的容量都不会变化. 那么之要每次增广到端点容量最小的那个点就好了.

我的思考:

设经过k次 我们完成了所有的删边操作,所以我们二分k

把所有无向边转换成有向边

我采取的是方法2 新增除了原先的n个节点之外 把每条边看成是新的节点 也就是说新增m个节点,然后跑一下最大流

建图

1.s向所有m个边节点 连一条容量为1的边 

2.每个边结点 向他的顶点的节点 连一条容量为1的边

3.每个顶点节点向t连一条容量为k的边,其中k是我们当前二分的答案,如果最大流==m 那么当前是可行方案

然后考虑为什么这样建边能满足题目所给的条件呢,题目要求删边,但每次删的边中不能存在两个环,这个在最大流计算中是这样体现的,我们进行k次删边,然后如果删的边中存在一个环 那么这样进行的一次删边会占据所有删的边的节点到t的边的流量1,然后如果删一个存在两个环的图的话,因为我们知道两个环的图 如果其中有n条边,那么他的节点只有n-1个,也就是说会有一个顶点节点到t的边容量会是2,2的意义也就是说要进行2次删边,所以原问题就完全转换成了上述网络流过程

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=2e3+10;
int T,n,m,x[N],y[N];
struct Maxflow
{
    const static int maxe=2e6+10;
    const static int maxp=1e5+10;
    const static int INF=0x7ffffff;
    struct node
    {
        int x,f;
        node(){}
        node(int x,int f):x(x),f(f){};
    }edge[maxe];
    int head[maxp],next[maxe],dist[maxp],tot,work[maxp],n;
    void clear(int x){n=x;tot=0;for(int i=0;i<=n;i++) head[i]=-1;}
    void addedge(int s,int t,int f)
    {
        edge[tot]=node(t,0);next[tot]=head[s];head[s]=tot++;
        edge[tot]=node(s,f);next[tot]=head[t];head[t]=tot++;
    }
    bool bfs(int s,int t)
    {
        for (int i=0;i<=n;i++)
            dist[i] = -1;
        queue<int>p;
        p.push(s);
        dist[s]=0;
        while(!p.empty())
        {
            int q=p.front();p.pop();
            for(int i=head[q];i!=-1;i=next[i])
            {
                if (edge[i^1].f&&dist[edge[i].x] ==-1)
                {
                    p.push(edge[i].x);
                    dist[edge[i].x]=dist[q]+1;
                    if(dist[t]!=-1) return true;
                }
            }
        }
        return false;
    }
    int dfs(int s,int t,int low)
    {
        if(s==t)return low;
        for (int &i=work[s],x;i>=0;i=next[i])
        {
            if(dist[s]+1==dist[edge[i].x]&&edge[i^1].f&&(x=dfs(edge[i].x,t,min(low,edge[i^1].f))))
            {
                edge[i].f+= x;
                edge[i^1].f-=x;
                return x;
            }
        }
        return 0;
    }
    int dinic(int s,int t)
    {
        int maxflow=0,inc=0;
        while (bfs(s,t))
        {
            for(int i=0;i<=n;i++) work[i]=head[i];
            while(inc=dfs(s,t,INF)) maxflow+=inc;
        }
        return maxflow;
    }
}solve;
bool check(int flow)
{
    solve.clear(n+m+1);
    for(int i=1;i<=m;i++)
    {
        solve.addedge(i,0,1);
        solve.addedge(m+x[i],i,1);
        solve.addedge(m+y[i],i,1);
    }
    for(int i=m+1;i<=m+n;i++) solve.addedge(n+m+1,i,flow);
    return solve.dinic(n+m+1,0)==m;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]);
        if(m==0) {printf("0\n");continue;}
        int left=1,right=m;
        while(left<=right)
        {
            int mid=(left+right)/2;
            if(check(mid)) right=mid-1;else left=mid+1;
        }
        printf("%d\n",left);
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值