CF883ABDK

版权声明:原来这里可以拿来卖萌ヽ(・∀・)ノ https://blog.csdn.net/u012345506/article/details/80223752

A.
每次关门后找到最近的 x a 以及 t y
t y < x a ,直接模拟即可;
否则,取 A = M I N { t y a , n } ,计算从 x a 开始,不断地开关门直到最后一次关门的时间不小于 A a
的次数。容易计算这个次数为 A x + 1 d a + 1 ,之后直接模拟能取多少个 t 即可。
总复杂度为 O ( m )

#include<functional>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<cmath>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
//--Container
//
typedef long long ll;
const int up=1e5;ll ar[up+10];
void cl(){
    ll i,j,k,t,z,n,m,a,d,_a,_b,r[2];scanf("%I64d %I64d %I64d %I64d",&n,&m,&a,&d);
    for(i=0;i<m;scanf("%I64d",&ar[i++]));
    for(clr(r),k=0,t=d/a+1;r[0]<n||r[1]<m;){
        if(r[0]>=n){for(++k,i=r[1];r[1]<m&&ar[r[1]]-ar[i]<=d;++r[1]);}
        else if(r[1]>=m){k+=(n-r[0])/t+((n-r[0])%t?1:0);break;}
        else{
            _a=(r[0]+1)*a,_b=ar[r[1]];
            if(_b<=_a){
                for(++k;r[1]<m&&ar[r[1]]-_b<=d;++r[1]);
                for(;r[0]<n&&(r[0]+1)*a-_b<=d;++r[0]);
            }
            else{
                j=min(n,_b/a);z=(j-r[0])/t+((j-r[0])%t?1:0);k+=z,r[0]+=z*t;
                for(_a=(r[0]-t+1)*a;r[1]<m&&ar[r[1]]-_a<=d;++r[1]);
            }
        }
    }
    printf("%I64d\n",k);
};

int main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif
    cl();
    return 0;
};

B.
显然的,若要有解,这个有向图必须是一个DAG图。
对每个节点 v ,其都有一个合法的取值区间 [ l v , r v ] 。很容易由 v 到出度为0的点及入度为0的点的最长距离得到这个区间。
为了方便,我们可以将他们转换为递推关系: l v = M A X x c h i l d v { l x + 1 } , r v = M I N x f a t h e r v { r x 1 }
对于已经给定了权值 w g v 的节点 v ,那么其合法区间为 [ w g v , w g v ]

容易发现对于任意父子节点, l f a t h e r > l c h i l d , r f a t h e r > r c h i l d 。故在子节点的合法区间中任意地取值并不会影响父节点在更新 l 之后的合法性,因为最终其必然满足 l r
考虑如何在这些区间中取值,使得最终取值能够占满 [ 1 , k ] 。我们将 r 值从小到大进行处理,在 r 相同时,将 l 从小到大进行处理,这同时使得我们必然先处理子节点,后处理父节点。
对于一个区间 [ l i , r i ] ,我们找到最小的 x 使其满足 x [ l i , r i ]
考虑区间 [ l i , x ) ,显然这段区间对结果已经不再有任何帮助,我们直接将其消去,那么对于其他所有最小可取值为 x 的区间 j ,有 l i = l j = x , r i r j ,显然,此时我们应当取 x 值获得最优解。而对于其他最小可取值小于 x 的区间 j ,在其中取得 x 值必然不会比最优解更优,因为我们完全可以在 i 中取得 x 值,而在 j 取得另一个新的值,最小可取值大于 x 的区间同理。
故此时我们应当直接取 x 作为该节点的值,对于 x 不存在的节点,随意赋一个 [ l i , r i ] 中的值即可。

O ( n ) 求出节点的合法区间之后,按拓扑序将节点插入优先队列处理即可。总复杂度 O ( n l o g n )

#include<functional>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<cmath>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
//--Container
#include<queue>
#include<set>
//
typedef long long ll;
const int up=200000;

struct eg{int u,v,nx;}gp[(up<<1)+10];int cnt,hd[(up<<1)+10],dl[up+10],dr[up+10],n,m,dk,bd[up+10],wg[up+10],in[up+10],ou[up+10],tmp[up+10];
inline void psh(int u,int v){++cnt;gp[cnt].v=v,gp[cnt].u=u,gp[cnt].nx=hd[u],hd[u]=cnt;};
struct ti{int a;ti(int A=0):a(A){};
friend bool operator<(const ti&,const ti&);friend bool operator>(const ti&,const ti&);friend bool operator==(const ti&,const ti&);
};
bool operator<(const ti&a,const ti&b){return dr[a.a]==dr[b.a]?dl[a.a]<dl[b.a]:dr[a.a]<dr[b.a];};
bool operator>(const ti&a,const ti&b){return dr[a.a]==dr[b.a]?dl[a.a]>dl[b.a]:dr[a.a]>dr[b.a];};
bool operator==(const ti&a,const ti&b){return dl[a.a]==dl[b.a]&&dr[a.a]==dr[b.a];};

bool dfq(){
    int i,j;for(i=1;i<=n;++i)tmp[i]=ou[i];queue<int>qe;for(clr(bd),i=1;i<=n;++i)if(!tmp[i])qe.push(i);
    for(;!qe.empty();){
        int v=qe.front();qe.pop();bd[v]=1;for(i=hd[v+n];i;i=gp[i].nx){
            --tmp[gp[i].v];if(!tmp[gp[i].v])qe.push(gp[i].v);
        }
    }
    for(i=1;i<=n;++i)if(!bd[i])return 0;
    return 1;
};
bool dfx(int v){
    int i,j;bd[v]=1;for(dl[v]=0,i=hd[v];i;i=gp[i].nx){
        if(!bd[gp[i].v]&&!dfx(gp[i].v))return 0;
        dl[v]=max(dl[v],dl[gp[i].v]);
    }
    dl[v]++;
    if(dl[v]>dk)return 0;
    if(wg[v]&&wg[v]<dl[v])return 0;
    if(wg[v])dl[v]=wg[v];
    return 1;
};
bool dfz(){
    int i,j;queue<int>qe;for(clr(bd),i=1;i<=n;++i)if(!in[i])qe.push(i),dr[i]=wg[i]?wg[i]:dk;
    for(;!qe.empty();){
        int v=qe.front();qe.pop();if(dr[i]<dl[i])return 0;
        for(i=hd[v];i;i=gp[i].nx){
            if(!bd[gp[i].v]){bd[gp[i].v]=1,dr[gp[i].v]=up+233;}
            --in[gp[i].v],dr[gp[i].v]=min(dr[gp[i].v],dr[v]-1);
            if(!in[gp[i].v]){
                if(wg[gp[i].v]&&wg[gp[i].v]>dr[gp[i].v])return 0;
                if(wg[gp[i].v])dr[gp[i].v]=wg[gp[i].v];
                bd[gp[i].v]=1,qe.push(gp[i].v);
            }
        }
    }
    return 1;
};

void _cl(){
    int i,j,rs,d,k;priority_queue<ti,vector<ti>,greater<ti> >qe;for(i=1;i<=n;++i)if(!ou[i])qe.push(ti(i));
    set<int>st;for(i=1;i<=dk;st.insert(i++));
    for(rs=1;!qe.empty();){
        int v=qe.top().a;qe.pop();set<int>::iterator zt=st.lower_bound(dl[v]);
        if(zt==st.end()||*zt>dr[v])dl[v]=dr[v];
        else
            dl[v]=*zt,st.erase(zt);
        for(i=hd[v+n];i;i=gp[i].nx){
            dl[gp[i].v]=max(dl[gp[i].v],dl[v]+1);
            --ou[gp[i].v];if(!ou[gp[i].v])qe.push(gp[i].v);
        }
    }
    if(!st.empty())printf("-1\n");
    else{
        for(printf("%d",dl[1]),i=2;i<=n;printf(" %d",dl[i++]));putchar('\n');
    }
};

void cl(){
    int i,j,k,a,b;bool fg=1;scanf("%d %d %d",&n,&m,&dk);
    for(cnt=0,clr(hd),i=1;i<=n;scanf("%d",&wg[i++]));
    for(i=0;i<m;++i){scanf("%d %d",&a,&b);psh(a,b);psh(b+n,a);++in[b],++ou[a];}
    if(!dfq()){printf("-1\n");return;}
    for(clr(bd),i=1;i<=n;++i)if(!in[i]&&!dfx(i))fg=0;
    if(!fg||!dfz()){printf("-1\n");return;}
    _cl();
};

int main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif
    cl();
    return 0;
};

D.
若只有一个 P 点,直接计算即可。
考虑二分最短时间 t ,那么问题变成了是否能够为所有的 P 点选取 [ P i t , P i ] , [ P i , P i + t ] 中的一个,使得所有的区间覆盖所有的 点。
f ( i ) 表示前 i P 点能覆盖区间 [ 1 , f ( i ) ] 中的所有 点。在转移时,两个区间相交的情况较为特殊,但可以发现,若 P i , P i 1 两个点产生的区间相交,那么前 P i 2 个点必须覆盖一个完整的前缀中所有的 点,否则之后的点无法再覆盖到这些空缺。
故直接转移即可,总复杂度 O ( n l o g n )

#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<cmath>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
//--Container
#include<set>
//
typedef long long ll;
const int up=1e6;
int n,dr[up+10],pr[up+10],dp[up+10],pn;char cz[up+10];
inline int _sg(int l,int r){return l<=r?dr[r]-dr[l-1]:0;};
bool _cl(int p){
    int i,j,k;dp[0]=0;if(_sg(max(1,pr[0]-p),pr[0])==_sg(1,pr[0]))dp[0]=pr[0];
    if(_sg(pr[0],min(n,pr[0]+p))==_sg(1,min(n,pr[0]+p)))dp[0]=min(pr[0]+p,n);
    for(i=1;i<pn;++i){
        dp[i]=dp[i-1];if(pr[i]-p<=dp[i-1]||!_sg(dp[i-1]+1,pr[i]-p-1))dp[i]=max(dp[i],pr[i]);
        if(pr[i]<=dp[i-1]||!_sg(dp[i-1]+1,pr[i]-1))dp[i]=min(n,pr[i]+p);
        if(pr[i]<=pr[i-1]+p&&(max(1,pr[i]-p)<=(i-2>=0?(!dp[i-2]?1:dp[i-2]):1)||!_sg(i-2>=0?dp[i-2]+1:1,pr[i]-p-1)))dp[i]=max(dp[i],min(n,pr[i-1]+p));
    }
    return !_sg(dp[pn-1]+1,n);
};

void cl(){
    int i,j,k,d,b,e,t;scanf("%d",&n);scanf("%s",cz+1);
    for(i=1,j=0;i<=n;++i)if(cz[i]=='P')++j;
    if(j==1){
        for(j=0,i=1;cz[i]!='P';++i)if(cz[i]=='*')++j;
        for(t=i,k=0;i<=n;++i)if(cz[i]=='*')++k;
        for(b=1;cz[b]!='*';++b);
        for(e=n;cz[e]!='*';--e);
        if(j<k){
            printf("%d %d\n",k,e-t);
        }
        else if(k<j){
            printf("%d %d\n",j,t-b);
        }
        else{
            printf("%d %d\n",k,min(e-t,t-b));
        }
    }
    else{
        for(dr[0]=0,i=1;i<=n;++i)dr[i]=dr[i-1]+(cz[i]=='*'?1:0);
        for(pn=0,i=1;i<=n;++i)if(cz[i]=='P')pr[pn++]=i;
        for(b=0,e=n;b<=e;){
            t=(b+e)>>1;if(_cl(t))d=t,e=t-1;
            else
                b=t+1;
        }
        for(j=0,i=1;i<=n;++i)if(cz[i]=='*')++j;
        printf("%d %d\n",j,d);
    }
};

int main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif
    cl();
    return 0;
};

K.
采取如下方式得到 [ l , r ] 的最优解:
将区间 [ l , r ] 所有值加1,寻找 [ l + 1 , r 1 ] 中所有不能再加的点 p ,即该点的值等于 s p + g p ,继续对区间 [ l + 1 , p 1 1 ] , [ p 1 + 1 , p 2 1 ] . . . 进行相同操作。
因为 i = 0 r l + 1 2 r l + 1 2 i 必然是区间 [ l , r ] 所能得到的最优解,故上述方法可以得到最优解。
考虑如何实现这个过程,可以发现,当这个过程结束时,这个序列由递增递减交替的序列组成,而递增的起点与递减的终点都是某些点可取的最大值,即 s i + g i 。故我们可以先从前往后构造出递增的段,然后再从后往前修复递减的段,即可得到最终的结果。总复杂度为 O ( n )

#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<cmath>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
//--Container
#include<set>
//
typedef long long ll;
const int up=200000;int ar[up+10],br[up+10];
void cl(){
    int i,j,k,d,t,a,b,n;scanf("%d",&n);for(i=1;i<=n;++i){
        scanf("%d %d",&ar[i],&br[i]);br[i]+=ar[i];
    }
    for(i=2;i<=n;++i)br[i]=min(br[i-1]+1,br[i]);
    for(i=n-1;i;--i)br[i]=min(br[i+1]+1,br[i]);
    bool fg=1;for(i=1;i<=n;++i)if(br[i]<ar[i])fg=0;
    for(i=2;i<=n;++i)if(abs(br[i]-br[i-1])>1)fg=0;
    ll rs=0;for(i=1;i<=n;++i)rs+=br[i]-ar[i];
    if(!fg)printf("-1\n");
    else{
        for(printf("%I64d\n",rs),printf("%d",br[1]),i=2;i<=n;printf(" %d",br[i++]));putchar('\n');
    }
};

int main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif
    cl();
    return 0;
};

猜你喜欢

转载自blog.csdn.net/u012345506/article/details/80223752
cf