BestCoder Round #74

身败名裂啊。。。。。。

T1WA了半天,30min才A。

T2又WA了一发,然后Hack刚2min就被别人叉了。

T3做完后最后40min不知所措。

去叉别人,看到一个人写D题判m=0很奇怪,随手把他叉了:

1

3 0

然后发现,标程输出1.

奥妙重重

嘿嘿嘿,我竟然又去叉了吉利。

T1的SB错误是,答案可能是|x-y|。

T2的SB错误是,一个[1,10^5]的数异或[1,10^5]的数可能是[1,131072]的数。

T1 Shortest Path

x到y的路径可能有几种:
1.x->y

2.x->i->j>y

其中i和j是新边里的节点(称为关键节点)

将六个关键点连起来跑一下Floyd,然后每次询问时枚举一下i,j。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<map>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
using namespace std;
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const int maxn=100010;
int n,m,u[5],v[5],A[10],cnt;
int W[10][10];
map<int,int> M;
int find(int x) {
    if(!M.count(x)) M[x]=++cnt,A[cnt]=x;
    return M[x];
}
const int mod=1000000007;
int main() {
    dwn(T,read(),1) {
        n=read();m=read();M.clear();cnt=0;
        rep(i,1,6) rep(j,1,6) W[i][j]=100000000;
        rep(i,1,6) W[i][i]=0;
        rep(i,1,3) u[i]=read(),v[i]=read(),W[find(u[i])][find(v[i])]=W[find(v[i])][find(u[i])]=1;
        rep(i,1,cnt) rep(j,1,cnt) W[i][j]=min(W[i][j],abs(A[i]-A[j]));
        rep(k,1,cnt) rep(i,1,cnt) rep(j,1,cnt) W[i][j]=min(W[i][j],W[i][k]+W[k][j]);
        long long res=0;
        rep(i,1,m) {
            int x=read(),y=read(),ans=abs(x-y);
            rep(k,1,cnt) rep(j,1,cnt) ans=min(ans,abs(A[k]-x)+abs(A[j]-y)+W[k][j]);
            (res+=(long long)i*ans)%=mod;
        }
        printf("%I64d
",res);
    }
    return 0;
}
View Code

T2 Transform

x -> y  <=> 0 -> x xor y

所以从0BFS一下就好了。

然后应该搜到[1,131072]。QAQ(AQ)*

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<queue>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
using namespace std;
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const int maxn=300010;
const int mod=1000000007;
typedef long long ll;
int n,m,A[maxn],vis[maxn],d[maxn];
queue<int> Q;
void bfs() {
    memset(vis,0,sizeof(vis));d[0]=0;vis[0]=1;Q.push(0);
    while(!Q.empty()) {
        int x=Q.front();Q.pop();
        rep(i,0,20) {
            int v=x^(1<<i);
            if(v<=300000&&!vis[v]) {
                vis[v]=1;d[v]=d[x]+1;Q.push(v);
            }
        }
        rep(i,1,n) {
            int v=x^A[i];
            if(v<=300000&&!vis[v]) {
                vis[v]=1;d[v]=d[x]+1;Q.push(v);
            }
        }
    }
}
int main() {
    dwn(T,read(),1) {
        n=read();m=read();
        rep(i,1,n) A[i]=read();
        bfs();ll ans=0;
        rep(i,1,m) (ans+=(ll)i*d[read()^read()])%=mod;
        printf("%I64d
",ans);
    }
    return 0;
}
View Code

T3 Toposort

我们逐位考虑,那么我们需要一个数据结构完成这样的功能。

1.找到当前权值<=k的最小节点。

2.将一个节点的权值-1。

显然线段树就行了。

我懒得写线段树,于是STL大法好。

用一个堆来维护一下在某次操作前权值<=k的节点,不行就把它踢出去。

然后拓扑的时候再判判能不能扔到堆里。

因为最多有n+m个节点进入堆,所以时间复杂度为O((n+m)logn)。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<queue>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
using namespace std;
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const int mod=1000000007;
const int maxn=100010;
const int maxm=200010;
typedef long long ll;
int n,m,k,first[maxn],Next[maxm],to[maxm],in[maxn],is[maxn],e;
void AddEdge(int u,int v) {
    to[++e]=v;Next[e]=first[u];first[u]=e;in[v]++;
}
int main() {
    dwn(T,read(),1) {
        n=read();m=read();k=read();
        rep(i,1,n) in[i]=first[i]=is[i]=0;e=0;
        rep(i,1,m) {
            int u=read(),v=read();
            AddEdge(u,v);
        }
        priority_queue<int> Q;
        rep(i,1,n) if(in[i]<=k) is[i]=1,Q.push(-i);
        ll ans=0;
        rep(j,1,n) {
            int x;
            while(Q.size()) {
                x=-Q.top();Q.pop();is[x]=0;
                if(in[x]<=k) break;
            }
            (ans+=(ll)j*x)%=mod;k-=in[x];in[x]=0;is[x]=1;
            for(int i=first[x];i;i=Next[i]) if(in[to[i]]) {
                in[to[i]]--;
                if(in[to[i]]<=k&&!is[to[i]]) is[to[i]]=1,Q.push(-to[i]);
            }
        }
        printf("%I64d
",ans);
    }
    return 0;
}
View Code

T4 Deletion

比赛时并没有想出来QAQ(AQ)*

这道题想法很妙,考虑题目说的“要求选出来的边构成的子图的每个连通块最多只有一个环”其实就是环套树。

而如果对一个无向图进行定向,满足所有节点的出边最多只有1,那么这个无向图可以一次选出。

所以问题转化成对一个无向图进行定向,让出边最多的节点出度最少,那么二分答案后就是一个分配模型了。

#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i!=-1;i=Next[i])
using namespace std;
typedef long long ll;
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const int maxn=5010;
const int maxm=100010;
const int inf=1e9;
struct Dinic {
    int n,m,s,t,first[maxn],Next[maxm];
    void init(int n) {
        m=0;this->n=n;
        memset(first,-1,sizeof(first));
    }
    struct Edge {int from,to,flow;}edges[maxm];
    void AddEdge(int u,int v,int w) {
        edges[m]=(Edge){u,v,w};Next[m]=first[u];first[u]=m++;
        edges[m]=(Edge){v,u,0};Next[m]=first[v];first[v]=m++;
    }
    int d[maxn],vis[maxn],cur[maxn];
    int BFS() {
        memset(vis,0,sizeof(vis));
        queue<int> Q;Q.push(s);vis[s]=1;
        while(!Q.empty()) {
            int x=Q.front();Q.pop();cur[x]=first[x];
            ren {
                Edge& e=edges[i];
                if(!vis[e.to]&&e.flow) {
                    vis[e.to]=1;
                    d[e.to]=d[x]+1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int DFS(int x,int a) {
        if(x==t||!a) return a;
        int flow=0,f;
        for(int& i=cur[x];i!=-1;i=Next[i]) {
            Edge& e=edges[i];
            if(d[e.to]==d[x]+1&&(f=DFS(e.to,min(a,e.flow)))) {
                e.flow-=f;edges[i^1].flow+=f;
                a-=f;flow+=f;if(!a) break;
            }
        }
        return flow;
    }
    int solve(int s,int t) {
        this->s=s;this->t=t;int flow=0;
        while(BFS()) flow+=DFS(s,inf);
        return flow;
    }
}sol;
int n,m,u[maxn],v[maxn];
int check(int x) {
    int s=n+m+1,t=n+m+2;sol.init(t);
    rep(i,1,n) sol.AddEdge(s,i,x);
    rep(i,1,m) sol.AddEdge(i+n,t,1);
    rep(i,1,m) sol.AddEdge(u[i],i+n,1),sol.AddEdge(v[i],i+n,1);
    return sol.solve(s,t)==m;
}
void solve() {
    n=read();m=read();
    rep(i,1,m) u[i]=read(),v[i]=read();
    if(!m) {puts("0");return;}
    int l=1,r=m,mid;
    while(l<r) if(check(mid=l+r>>1)) r=mid; else l=mid+1;
    printf("%d
",l);
}
int main() {
    dwn(T,read(),1) solve();
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/5248571.html