BestCoder Round #74

来源:互联网 发布:红色警戒mac迅雷下载 编辑:程序博客网 时间:2024/06/07 14:56

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 __int64int 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 __int64using 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
原创粉丝点击