Sticey_training#1[HARD]

已补 2 / 7 2/7 2/7

CodeForces - 510E

题意

给定多个数,形成多个环,保证环上相邻和是素数。

题解

刘大爷提示网络流之后就开始往那方面想。
把题目转换成,找 n n n条边,并且保证每个点只和两条边相连。
但是怎么建图都只能限制度数或者边数。

题解是采用了素数=奇数+偶数。也就是每条选择的边一定是奇数和偶数。
设置一个二分图,左边奇数,右边偶数,一旦选择一条边,就会用掉一个点的度数。选择 n n n条边就会用掉 2 n 2n 2n的度数。这样可以保证点的度数不会出现只用掉了边上的一个点的度数,而且两个都计算了。
非常巧妙。

之前设置的边数是对的,但是可能会出现某个点度数过大(以边连点)。
反过来以点连边,需要有些边必须被选择两次,而实际可能只跑一个流量。
两者都是不可取的。

#include<bits/stdc++.h>
using namespace std;

const int maxn = 100050;
#define inf 0x3f3f3f3f
typedef long long ll;

struct Edge{
    int from,to;
    ll cap,flow;
};

vector<int>T[maxn];
vector<int>ans[maxn];
bool vis[maxn];

struct Dinic{
    int n,tmp,s,t,ttt;
    vector<Edge>edges;
    vector<int>G[maxn];//邻接表用,存储的是边在edges中的序号
    bool vis[maxn];//BFS使用
    int d[maxn];//从起点到i的距离
    int cur[maxn];//当前弧下标

    void init(int n,int s,int t,int tttt){
        this->n=n,this->s=s,this->t=t,this->ttt=tttt;
        edges.clear();
        for(int i=1;i<=n;i++)G[i].clear();
    }

    void AddEdge(int from,int to,ll cap){
        edges.push_back((Edge){from,to,cap,0});
        edges.push_back((Edge){to,from,0,0});
        tmp=edges.size();
        G[from].push_back(tmp-2);
        G[to].push_back(tmp-1);
    }

    bool BFS(){
        memset(vis,0,sizeof(vis));
        queue<int>q;
        q.push(s);
        d[s]=0,vis[s]=1;
        while(!q.empty()){
            int x=q.front();q.pop();
            for(int i=0;i<G[x].size();i++){
                Edge& e = edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow){
                    vis[e.to]=1;
                    d[e.to]=d[x]+1;
                    q.push(e.to);
                }//只考虑残量网络中的弧
            }
        }
        return vis[t];//用于判断是否能走到底。
    }

    ll DFS(int x,ll a){//多路增广
        if(x==t||a==0)return a;//a表示的是当前最小,也就是接下来能用的不能超过a
        ll flow=0,f;
        for(int& i=cur[x];i<G[x].size();i++){//能保证一个dfs中不重复走同样的边(对于同一个节点),因为走过的边一定是满载的了。
            Edge& e = edges[G[x][i]];
            if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){
                e.flow+=f;
                edges[G[x][i]^1].flow-=f;
                flow+=f;
                a-=f;
                if(a==0)break;//f表示从这个e.to的点开始使用的最大流。
            }
        }
        return flow;
    }

    ll Maxflow(){
        ll flow=0;
        while(BFS()){
            memset(cur,0,sizeof(cur));
            flow+=DFS(s,inf);
        }
        return flow;
    }
    bool del(){
        if(Maxflow()!=ttt)return false;
        for(int i=1;i<=ttt;i++){
            for(int j=0;j<G[i].size();j++){
                int f=edges[G[i][j]].flow;
                int v=edges[G[i][j]].to-ttt;
                if(v>=1&&v<=ttt);
                else continue;
                if(f){
                    T[i].push_back(v);
                    T[v].push_back(i);
                }
            }
        }
        return true;
    }
}dc;

int A[maxn];

void dfs(int u,int now){
    ans[now].push_back(u);
    vis[u]=true;
    for(auto v:T[u]){
        if(vis[v])continue;
        dfs(v,now);
    }
}

int main(){
    int n;cin>>n;
    for(int i=1;i<=n;i++)scanf("%d",&A[i]);
    dc.init(2*n+2,2*n+1,2*n+2,n);
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            int now=A[i]+A[j];
            bool ok=true;
            for(int k=2;k<=sqrt(now);k++){
                if(now%k==0)ok=false;
            }
            if(ok){
                int l,r;
                if(A[i]%2)l=i,r=n+j;
                else l=j,r=n+i;
                //cout<<l<<" "<<r-n<<endl;
                dc.AddEdge(l,r,1);
            }
        }
    }
    for(int i=1;i<=n;i++){
        if(A[i]%2){
            dc.AddEdge(2*n+1,i,2);
        }
    }
    for(int i=1;i<=n;i++){
        if(A[i]%2==0){
            dc.AddEdge(n+i,2*n+2,2);
        }
    }
    int cnt=0;
    if(dc.del()){
        for(int i=1;i<=n;i++){
            if(vis[i])continue;
            dfs(i,++cnt);
        }
        cout<<cnt<<endl;
        for(int i=1;i<=cnt;i++){
            cout<<ans[i].size()<<" ";
            for(auto x:ans[i])printf("%d ",x);
            puts("");
        }
    }
    else puts("Impossible");
}

CodeForces - 518E

题意

对于 a a a数组,有生成新序列 ( a 1   +   a 2...   +   a k ,   a 2   +   a 3   +   . . .   +   a k   +   1 ,   . . . ,   a n   −   k   +   1   +   a n   −   k   +   2   +   . . .   +   a n ) (a1  +  a2 ...  +  ak,  a2  +  a3  +  ...  +  ak + 1,  ...,  an - k + 1  +  an - k + 2  +  ...  +  an) (a1+a2...+ak,a2+a3+...+ak+1,...,ank+1+ank+2+...+an)
但原数组存在未知数 ? ? ?并且有多个,要求新序列是严格递增的,构造一个合法原序列(赋值 ? ? ?)
要求 ? ? ?的绝对值和最小。

题解

简单推一下可以推出 a i < a k + i a_i<a_{k+i} ai<ak+i
也就是只需要维护 k k k个递增序列。
对于每个递增序列 l , ? , . . . , ? , r l,?,...,?,r l,?,...,?,r的情况。
如果区间全正全负,显然就是往 0 0 0靠。
如果区间过 0 0 0且大小合适,就是以中间往左右扩展。

具体做法就是:从 l l l开始往右,从 r r r开始往左,判断是否能扩展,从而在中间往右扩展,由于奇数的情况就采用两种办法判合法即可(其实可能不要)。

#include<bits/stdc++.h>
using namespace std;

const int maxn = 5e5+5000;

typedef long long ll;
int n,k,now,pre;
int A[maxn],vis[maxn];
vector<int>G;

int main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        string str;cin>>str;
        if(str=="?")vis[i]=1;
        else{
            if(str[0]=='-'){
                str=str.substr(1);
                A[i]=-atoi(str.c_str());
            }
            else A[i]=atoi(str.c_str());
        }
    }
    for(int i=1;i<=k;i++)A[n+i]=0x3f3f3f3f;
    bool ok=true;
    for(int i=1;i<=k;i++){
        now=0,G.clear();
        pre=-0x3f3f3f3f;
        for(int j=i;j<=n+k;j+=k){
            if(vis[j])now++,G.push_back(j);
            else{
                //cout<<j<<" "<<A[j]<<" "<<now<<" "<<ok<<endl;
                if(now==0){
                    if(A[j]>pre){
                        pre=A[j];
                        continue;
                    }
                    else{
                        ok=false;
                        break;
                    }
                }
                int r=max(A[j],pre),l=min(A[j],pre);
                int RR,LL;
                ll ans=0,tmp;
                if(r-l-1>=now){
                    RR=r,LL=l;
                    for(int x=now-1;x>=0;x--){
                        A[G[x]]=--RR;
                        ans+=abs(A[G[x]]);
                    }
                    RR=r,LL=l,tmp=0;
                    for(int x=0;x<now;x++)tmp+=abs(++LL);
                    if(tmp<ans){
                        ans=tmp;
                        LL=l;
                        for(int x=0;x<now;x++)A[G[x]]=++LL;
                    }
                    int st;
                    st=-now/2;
                    if(l<-now/2&&-now/2+now-1<r){
                        tmp=0;
                        for(int x=0;x<now;x++)tmp+=abs(st++);
                        if(tmp<ans){
                            ans=tmp;
                            st=-now/2;
                            for(int x=0;x<now;x++)A[G[x]]=st++;
                        }
                    }
                    st=now/2;
                    if(l<now/2-(now-1)&&now/2<r){
                        tmp=0;
                        for(int x=now-1;x>=0;x--)tmp+=abs(st--);
                        if(tmp<ans){
                            ans=tmp;
                            st=now/2;
                            for(int x=now-1;x<now;x++)A[G[x]]=st--;
                        }
                    }
                }
                else{
                    ok=false;
                    break;
                }
                pre=A[j];
                now=0;
                G.clear();
            }
        }
        if(ok==false)break;
    }
    for(int i=1;i<=k;i++){
        pre=-0x3f3f3f3f;
        for(int j=i;j<=n;j+=k){
            if(A[j]<=pre)ok=false;
            pre=A[j];
        }
    }
    if(ok){
        for(int i=1;i<=n;i++)printf("%d ",A[i]);
    }
    else puts("Incorrect sequence");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值