ARC 085

E
最小割模板题不会做系列。
其实问题就是每个数选择保留或者不保留各有一个花费,然后有nlogn个关系形如:如果i不保留,j也一定不能保留
正数的和为总价值,减去最小割就是答案

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e13
using namespace std;

const int maxn = 210;
const int maxm = 110000;

int n,st,ed;
int s[maxn];

struct edge{int y,nex;ll c;}a[maxm]; int len,fir[maxn];
inline void ins(const int x,const int y,const ll c)
{
    a[++len]=(edge){y,fir[x],c};fir[x]=len;
    a[++len]=(edge){x,fir[y],0};fir[y]=len;
}

queue<int>q;
int h[maxn];
bool bfs()
{
    for(int i=1;i<=ed;i++) h[i]=0;
    h[st]=1; q.push(st);
    while(!q.empty())
    {
        const int x=q.front(); q.pop();
        for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(a[k].c&&!h[y])
            h[y]=h[x]+1,q.push(y);
    }
    return h[ed]>0;
}
ll dfs(const int x,const ll flow)
{
    if(x==ed) return flow;
    ll delta=0;
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(a[k].c&&h[y]==h[x]+1)
    {
        ll mink=dfs(y,min(a[k].c,flow-delta));
        a[k].c-=mink; a[k^1].c+=mink;
        delta+=mink;
        if(delta==flow) return delta;
    }
    if(!delta) h[x]=0;
    return delta;
}
ll flow()
{
    ll re=0;
    while(bfs()) 
        re+=dfs(st,LLONG_MAX);
    return re;
}

ll re;

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&s[i]);
        if(s[i]>0) re+=(ll)s[i];
    }
    len=1; //
    st=n+1,ed=st+1;
    for(int i=1;i<=n;i++)
    {
        if(s[i]>0) ins(i,ed,(ll)s[i]);
        if(s[i]<0) ins(st,i,(ll)-s[i]);
        for(int j=i+i;j<=n;j+=i) ins(i,j,inf);
    }
    printf("%lld\n",re-flow());

    return 0;
}

F
不同的位最少可以转化成相同的位最多(不转直接做也行)
然后位置i,如果bi=1,覆盖他可以得到1的价值,bi=0,覆盖他可以得到-1的价值,问题变成若干个区间,每个区间选或不选,要求选出的区间并集的价值和最大。
将区间放到左端点处理,从1~n枚举左端点,假设当前枚举的左端点是i,设f[r]表示处理了1~i-1的左端点,区间最远覆盖到r的最大价值,线段树优化转移

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e9
using namespace std;

inline void up(int &x,const int &y){if(x<y)x=y;}
const int maxn = 210000;

int n,m;
int b[maxn],s[maxn],f[maxn];
vector<int>V[maxn];

int segf[maxn<<2],segd[maxn<<2];
void pushup(int seg[],const int x){ seg[x]=seg[x<<1]>seg[x<<1|1]?seg[x<<1]:seg[x<<1|1]; }
void build(const int x,const int l,const int r)
{
    if(l==r) { segd[x]=l>=0?-inf:0; return; }
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    build(lc,l,mid); build(rc,mid+1,r);
    pushup(segd,x);
}
int lx,rx,loc,c;
void upd(int seg[],const int x,const int l,const int r)
{
    if(l==r) { seg[x]=c; return; }
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    if(loc<=mid) upd(seg,lc,l,mid);
    else upd(seg,rc,mid+1,r);
    pushup(seg,x);
}
int query(int seg[],const int x,const int l,const int r)
{
    if(rx<l||r<lx) return -inf;
    if(lx<=l&&r<=rx) return seg[x];
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    return max(query(seg,lc,l,mid),query(seg,rc,mid+1,r));
}
int re;

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&b[i]),re+=!b[i];
    for(int i=1;i<=n;i++) s[i]=s[i-1]+(b[i]?1:-1);
    build(1,0,n);
    for(int i=1;i<=n;i++) f[i]=-inf;

    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        int l,r; scanf("%d%d",&l,&r);
        V[l].push_back(r);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<V[i].size();j++)
        {
            int r=V[i][j];
            lx=0,rx=i-1; int cc=query(segf,1,0,n);
            up(f[r],cc+s[r]-s[i-1]);

            lx=i-1,rx=r-1; cc=query(segd,1,0,n);
            up(f[r],cc+s[r]);

            loc=r,c=f[r];
            upd(segf,1,0,n);
            c=f[r]-s[r]; upd(segd,1,0,n);
        }
    }
    int ans=0;for(int i=1;i<=n;i++) up(ans,f[i]);
    printf("%d\n",n-(re+ans));

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值