Codeforces Round #600 (Div. 2) / contest 1253


题目地址:https://codeforces.com/contest/1253



A Single Push

题意:有两个一样长的数组a、b,问是否可以给a的某一段连续区间加上一个正整数(或者不进行操作),使得a与b相等。

思路:a与b按位作差,如果a最多存在一段连续的负数,则可以;否则不行。

代码

const int maxn=1e5+5;
int a[maxn],n;

int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    while(T--)
    {
        int n=read();
        REP(i,1,n) a[i]=read();
        REP(i,1,n) a[i]-=read();
        int i=1,temp=0;
        while(i<=n && !a[i]) i++;
        if(i<=n) temp=a[i];
        while(i<=n && a[i]<0 && a[i]==temp) i++;
        while(i<=n && !a[i]) i++;
        puts(i<=n?"NO":"YES");
    }

    return 0;
}



B Silly Mistake

题意:一个人进入公司的事件记为 id,出公司记为 -id,给定一个事件表,问该时间表是否可以划分为合理的若干天,如果可以输出天数以及每一天的事件数;否则输出-1 (如果某一天合理,则:每个人只能进入一次、人不能没有进入就出去、一天结束时公司为空)

思路

代码

int tot=0,ans[100005];

int main()
{
    //freopen("input.txt","r",stdin);
    int n=read();
    set<int> s,t;
    REP(i,1,n)
    {
        int x=read();
        if(x>0)
        {
            if(t.count(x))
            {
                if(!s.empty()) return puts("-1"),0;
                ans[tot++]=t.size()*2;
                t.clear();
            }
            else s.insert(x),t.insert(x);
        }
        else
        {
            x=-x;
            if(!s.count(x)) return puts("-1"),0;
            s.erase(x);
            if(s.empty()) ans[tot++]=t.size()*2,t.clear();
        }
    }
    if(!s.empty()) return puts("-1"),0;
    printf("%d\n",tot);
    REP(i,0,tot-1) printf("%d ",ans[i]);

    return 0;
}



C Sweets Eating

题意

思路:按照模m的余数分组进行递推就很好做了。

代码

const int maxn=2e5+5;
LL f[maxn],s[maxn];

int main()
{
    //freopen("input.txt","r",stdin);
    int n=read(),m=read();
    REP(i,1,n) s[i]=read();
    sort(s+1,s+n+1);
    REP(i,1,n) s[i]+=s[i-1];
    REP(i,1,m) f[i]=s[i];
    REP(i,1,m) for(int j=i+m;j<=n;j+=m) f[j]=f[j-m]+s[j];
    REP(i,1,n) printf("%lld ",f[i]);

    return 0;
}



D Harmonious Graph

题意:给定一个无向图,问至少需要添加多少条边,使得该无向图的每个分支结点集的编号是连续的。

思路:用并查集记录联通信息,同时记录每个父结点所在集合的大小,当合并时优先合并到编号小的结点上,然后用自动机的思路顺序匹配,在匹配的过程中如果当前结点的父结点是一个新结点,而上一个父结点的匹配数目不足其集合大小时(说明后面还有结点能够匹配到上一个父结点),把当前结点与上一个父结点集合合并。

代码

const int maxn=2e5+5;
int far[maxn],n,m,num[maxn];

int findd(int x){return x==far[x]?x:far[x]=findd(far[x]);}
void unite(int x,int y)
{
    int fx=findd(x),fy=findd(y);
    if(fx==fy) return;
    if(fx<fy) num[fx]+=num[fy],far[fy]=fx;
    else num[fy]+=num[fx],far[fx]=fy;
}

int main()
{
    //freopen("input.txt","r",stdin);
    n=read(),m=read();
    REP(i,1,n) far[i]=i,num[i]=1;
    while(m--)
    {
        int u=read(),v=read();
        unite(u,v);
    }
    int ans=0,pipei=0,temp=0,i=1;
    while(i<=n)
    {
        if(!temp) temp=findd(i),pipei=0;
        else if(findd(i)==temp) pipei++,i++;
        else if(pipei<num[temp]) unite(temp,i),ans++;
        else temp=0;
    }
    cout<<ans;

    return 0;
}



E Antenna Coverage

题意:有若干个(n个)天线覆盖范围为 [ x i s i , x i + s i ] [x_i-s_i,x_i+s_i] ,每花费 1 可以使其中一个天线覆盖范围 +1( s i s_i 加 1),问使得所有天线能够覆盖 [ 1 , m ] [1,m] 的最少花费是多少。( n 80 , m 1 e 5 n\leq 80,m\leq 1e5

思路:dp。设 f[i] 表示覆盖 [1, i] 的最少花费,对于当前的 i,如果 i 在任意一个区间内,则 f[i] = f[i-1] ;否则,枚举所有区间:首先一定有 f [ i ] f [ i 1 ] + 1 f[i]\leq f[i-1]+1 ,也即直接把 i-1 的那个区间扩展即可,然后枚举时如果 i > x j i> x_j ,用 f [ m a x { 2 x j i 1 ,   0 } ] + i ( x j + s j ) f[max\{2*x_j-i-1, \ 0\}]+i-(x_j+s_j) 来更新答案;如果 i < x j i<x_j ,用 f [ i 1 ] + x j s j i f[i-1]+x_j-s_j-i 来更新答案。

代码

const int maxn=1e5+5;
int n,m,s[maxn],x[maxn],f[maxn];

int main()
{
    //freopen("input.txt","r",stdin);
    n=read(),m=read();
    REP(i,1,n) x[i]=read(),s[i]=read();
    REP(i,1,m)
    {
        int flag=0,minx=f[i-1]+1;
        REP(j,1,n)
        {
            if(i>=x[j]-s[j] && i<=x[j]+s[j]) {flag=1; break;}
            else if(x[j]<i) minx=min(minx,f[max(2*x[j]-i-1,0)]+i-x[j]-s[j]);
            else minx=min(minx,f[i-1]+x[j]-s[j]-1);
        }
        if(flag) f[i]=f[i-1];
        else f[i]=minx;
    }
    cout<<f[m];

    return 0;
}



F Cheap Robot

题意:一个n个结点,m条边的带边权简单联通无向图,其中1-k号结点为central结点。机器人在central结点可以充满电,而每经过一条边电量会减少边权值。有q组询问,每组询问查询从结点u到结点v(保证u,v为central,即一开始电量是满的),能够到达的最少电量容量c是多少。n,m,q都是1e5级别的。

思路:先用Dijkstra处理数组dis,其意义为dis[i]表示结点i到某一个central的最短距离。假设机器人当前在结点i,剩余电量为x,那么必定成立 d i s [ i ] x c d i s [ i ] dis[i]\leq x\leq c-dis[i] ,左边不等式是因为目的结点为central结点,右边不等式成立是因为起始结点为central结点。换句话说,只要满足 d i s [ i ] x dis[i]\leq x ,我们在i结点处就可以让剩余电量取得最大值 c d i s [ i ] c-dis[i] ,因为可以跑到最近的central去充满电然后再跑回来。我们考虑机器人从u到v经过一条边(u, v, w),在u处的剩余电量为 c-dis[u],所以要满足 c d i s [ u ] w d i s [ v ] c-dis[u]-w \geq dis[v] ,即 d i s [ u ] + d i s [ v ] + w c dis[u]+dis[v]+w \leq c

那么我们就可以把 dis[u]+dis[v]+w 看成某一条边的边权,所以等价于要使得无向图两点之间最大边权最小,用 Kruskal重构树+树链剖分+LCA就可以求解。(类似于 货车运输 这道题,这道题是要使两点之间最小边权最大)

代码

const int maxn=2e5+5;
const LL inf=1ll<<50;
struct edge {int u,v,w;} e[maxn<<1];
vector<int> G[maxn];
vector<edge> GG[maxn];
int n,k,m,q,root,cnt,far[maxn];
LL a[maxn],dis[maxn];
int siz[maxn],f[maxn],d[maxn],son[maxn],dfn[maxn],which[maxn],top[maxn];
bool cmp(edge x,edge y) {return dis[x.u]+dis[x.v]+x.w<dis[y.u]+dis[y.v]+y.w;}

int findd(int x){return x==far[x]?x:far[x]=findd(far[x]);}

void dfs1(int u,int fa,int depth)
{
    f[u]=fa; d[u]=depth; siz[u]=1; son[u]=0;
    REP(i,0,G[u].size()-1)
    {
        int v=G[u][i];
        if(v==fa) continue;
        dfs1(v,u,depth+1);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]]) son[u]=v;
    }
}

void dfs2(int u,int tf)
{
    top[u]=tf; dfn[u]=++cnt; which[cnt]=u;
    if(!son[u]) return;
    dfs2(son[u],tf);
    REP(i,0,G[u].size()-1)
    {
        int v=G[u][i];
        if(v!=f[u] && v!=son[u]) dfs2(v,v);
    }
}

int LCA(int x,int y)
{
    while(top[x]!=top[y])
        d[top[x]]>d[top[y]]?(x=f[top[x]]):(y=f[top[y]]);
    return d[x]<d[y]?x:y;
}

bool vis[maxn];
void get_dis()
{
    typedef pair<LL,int> P;
    priority_queue<P,vector<P>,greater<P> > que;
    REP(i,1,k) que.push(P(dis[i],i));
    REP(i,k+1,n) dis[i]=inf;
    while(!que.empty())
    {
        P p=que.top(); que.pop();
        int d=p.first,u=p.second;
        if(vis[u]) continue;
        vis[u]=1;
        REP(i,0,GG[u].size()-1)
        {
            int v=GG[u][i].v,w=GG[u][i].w;
            if(vis[v]) continue;
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                que.push(P(dis[v],v));
            }
        }
    }
}

void add_edge(int u,int v) {G[u].push_back(v); G[v].push_back(u);}

void Kruskal()
{
    sort(e+1,e+m+1,cmp);
    REP(i,1,m)
    {
        int u=e[i].u,v=e[i].v;
        int fu=findd(u),fv=findd(v);
        if(fu==fv) continue;
        a[++n]=dis[u]+dis[v]+e[i].w;
        far[fu]=n; far[fv]=n; far[n]=n;
        add_edge(n,fu); add_edge(n,fv);
    }
}

int main()
{
    n=read(),m=read(),k=read(),q=read();
    REP(i,1,m)
    {
        int u=read(),v=read(),w=read();
        e[i]=(edge){u,v,w};
        GG[u].push_back((edge){u,v,w});
        GG[v].push_back((edge){v,u,w});
    }
    REP(i,1,n) far[i]=i;    // union-find set
    get_dis();
    Kruskal();
    dfs1(n,0,0);
    dfs2(n,n);
    while(q--)
    {
        int u=read(),v=read();
        printf("%lld\n",a[LCA(u,v)]);
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/dragonylee/article/details/105868073