黑吉辽沪冀晋六省联考 2017 BZOJ 4868&4869&4870&4871&4872&4873

趁着网络上题解还不是很多,赶快怒写一发骗一下访问量
这套题在BZOJ上的题号是4868~4873。
感觉还不错,就是有一些题弄起来有一点小恶心……
这套题的部分分给得都很多,很良心的QAQ


BZOJ 4868 [Shoi2017]期末考试

枚举+贪心

枚举i表示第i天出完,把i之后的贪心挪到i即可。

#include<cstdio>
#include<algorithm>
#define N 100005
using namespace std;
namespace runzhe2000
{
    typedef long long ll;
    ll A, B, C, ans = 1ll<<60;
    int cntn[N], cntm[N], n, m;
    void main()
    {
        ll cost = 0, dis = 0, lef = 0;
        scanf("%lld%lld%lld%d%d",&A,&B,&C,&n,&m); if(A>B)A=B;
        for(int i = 1; i <= n; i++) {int x; scanf("%d",&x); cntn[x]++; cost += N-x;}
        for(int i = 1; i <= m; i++) {int x; scanf("%d",&x); cntm[x]++; lef  += N-x;}
        for(int i = 2; i <  N; i++) cntn[i] += cntn[i-1], cntm[i] += cntm[i-1];
        for(int i = N-1; i; i--)
        {
            dis += (cntm[N-1] - cntm[i]);
            lef -= cntm[i];
            cost -= cntn[i];
            if(C >= 10000000000000000ll && cost) continue;
            ans = min(ans, min(dis,lef)*A+max(0ll,dis-lef)*B+cost*C);
        }
        printf("%lld\n",ans);
    }
}
int main()
{
    runzhe2000::main();
}

BZOJ 4869 [Shoi2017]相逢是问候

广义欧拉定理

我们假装c永远和模数p互质(不是题目里的c,p,是在一般情况下要做c ^ b % p的c和p),那根据欧拉定理,指数b就可以变成b%phi(p)。由于p是奇数则phi(p)必是偶数,p是偶数则phi(p)必至少减半,因此如果有形如:c ^ c ^ c ^…^ c ^ a,其实只要至多考虑前2log个指数即可,超过这些之后指数就恒为0,实际上就可以直接算出来,也就是一个定值,说明到后面就算不断修改这个点等于没改。然后我们直接暴力修改直到这个数不变,均摊是O(nlog)的。

如果c不和p互质,有一个东西叫广义欧拉定理(因为这东西降幂是O(log)级别的,因此也叫求幂大法),甩链接:http://blog.csdn.net/guhaiteng/article/details/52588223

注意用广义欧拉定理的时候必须当且仅当指数大于phi(p),否则不能直接加一个phi(p)上去……

然后这样子做,预处理理论上是O(nlog^3)的,实际上当迭代到很小的时候phi(p)会比较小,那我们记搜即可。还有就是虽然看上去要降幂2log次,实际上远不可达,实测迭代6层就可以通过本题……

不知道有没有别的复杂度更优秀的做法?

#include<cmath>
#include<iostream>
#include<cstdio>
#define N 50005
#define M 1000000
#define H 6
#define pr(_i) cout<<#_i<<" = "<<_i<<endl
using namespace std;
namespace runzhe2000
{
    typedef double db;
    typedef long long ll;
    int n, m, p, c, a[N], phi[H], pos[N], nocnt, vis[M][H], g[M][H], h[M][H], ex[H];
    struct node{int val, next;}no[N*H];
    int gcd(int a, int b){return b?gcd(b,a%b):a;}
    int fpow(int a, int b, int p, int &moded) // a ^ b mod phi[p]
    {
        bool flag = (a==c && b<M && p<H);
        if(flag && vis[b][p]) return moded = h[b][p], g[b][p];
        int r = 1, bb = b, tmpmoded = moded = 0;
        for(; b; b>>=1)
        {
            if(b&1) 
            {
                if((ll)r*a >= phi[p] || tmpmoded)  moded = 1, r = (ll)r*a%phi[p];
                else r *= a;
            }
            if((ll)a*a >= phi[p]) tmpmoded = 1, a = (ll)a*a%phi[p];
            else a *= a;
        }
        return flag ? (vis[bb][p] = 1, h[bb][p] = moded, g[bb][p] = r) : r;
    }
    int get_phi(int x)
    {
        int r = x;
        for(int i = 2, ii = sqrt((db)x); i <= ii; i++)  if(x % i == 0)
        {
            r = (ll) r * (i-1) / i;
            for(; x % i == 0; x /= i);
        }
        if(x != 1) r = (ll) r * (x-1) / x;
        return r;
    }
    int get_pow(int a, int cnt, int p, int &moded)
    {
        if(!cnt) return fpow(a, 1, p, moded);
        int pre = get_pow(a, cnt - 1, p + 1, moded);
        ex[p] && moded ? pre += phi[p+1] : 0;
        return fpow(c, pre, p, moded);
    }
    struct seg{int need, sum;}t[N*5];
    void pushup(int x)
    {
        t[x].sum = (t[x<<1].sum + t[x<<1|1].sum) % p;
        t[x].need = t[x<<1].need || t[x<<1|1].need;
    }
    void build(int x, int l, int r)
    {
        if(l == r){t[x].sum = no[pos[l]].val, t[x].need = no[pos[l]].next; return;} int mid = (l+r)>>1;
        build(x<<1,l,mid); build(x<<1|1,mid+1,r); pushup(x);
    }
    int query(int x, int l, int r, int ql, int qr)
    {
        if(ql <= l && r <= qr) return t[x].sum; int mid = (l+r)>>1, ret = 0;
        if(ql <= mid) (ret += query(x<<1, l, mid, ql, qr)) %= p;
        if(mid <  qr) (ret += query(x<<1|1, mid+1, r, ql, qr)) %= p;
        return ret;
    }
    void modi(int x, int l, int r, int ql, int qr)
    {
        if(!t[x].need) return; int mid = (l+r)>>1;
        if(l == r)
        {
            if(no[pos[l]].next)
            {
                pos[l] = no[pos[l]].next;
                t[x].sum = no[pos[l]].val;
                t[x].need = no[pos[l]].next;
            }
            return;
        }
        if(ql <= l && r <= qr)
        {
            if(t[x<<1].need) modi(x<<1,l,mid,ql,qr);
            if(t[x<<1|1].need) modi(x<<1|1,mid+1,r,ql,qr);
        }
        else
        {
            int mid = (l+r)>>1; 
            if(ql <= mid) modi(x<<1,l,mid,ql,qr); 
            if(mid <  qr) modi(x<<1|1,mid+1,r,ql,qr);
        }
        pushup(x);
    }
    void main()
    {
        scanf("%d%d%d%d",&n,&m,&p,&c);
        phi[0] = p;  ex[0] = gcd(p, c) != 1;
        for(int i = 1; i < H; i++) 
        {
            phi[i] = get_phi(phi[i-1]);
            ex[i] = gcd(phi[i], c) != 1;
        }
        for(int i = 1; i <= n; i++) 
        {
            scanf("%d",&a[i]);
            no[pos[i] = ++nocnt] = (node){a[i], 0};
            for(int j = 1, tmp; j < H; j++)
            {
                no[++nocnt] = (node){get_pow(a[i], j, 0, tmp), 0};
                no[nocnt-1].next = nocnt;
            }
        }
        build(1,1,n);
        for(; m--; )
        {
            int type, l, r; scanf("%d%d%d",&type,&l,&r);
            if(!type) modi(1,1,n,l,r);
            else printf("%d\n",query(1,1,n,l,r));
        }
    }
}
int main()
{
    runzhe2000::main(); 
}

BZOJ 4870 [Shoi2017]组合数问题

矩阵快速幂

考虑组合数的实际意义,那这个式子就是在求nk个数里取数满足取出的数个数模k为r的方案数。矩阵快速幂优化递推即可。

#include<cstdio>
#include<cstring>
#define N 55
using namespace std;
namespace runzhe2000
{
    typedef long long ll;
    int n, p, k, r;
    struct matrix
    {
        int a[N][N];
        matrix(){memset(a,0,sizeof(a));}
        matrix operator * (const matrix &that)
        {
            matrix r;
            for(int i = 0; i < k; i++)
                for(int j = 0; j < k; j++)
                    for(int l = 0; l < k; l++)
                        (r.a[i][j] += (ll) a[i][l] * that.a[l][j] % p) %= p;
            return r;
        }
        void show()
        {
            for(int i = 0; i < k; i++, puts(""))
                for(int j = 0; j < k; j++)
                    printf("%d ",a[i][j]);
            puts("");
        }
    }a, b;
    void main()
    {
        scanf("%d%d%d%d",&n,&p,&k,&r);
        for(int i = 0; i < k; i++)
        {
            a.a[i][(i+1)%k]++; a.a[i][i]++;
            b.a[i][i] = 1;
        }
        for(ll tmp = (ll) n * k; tmp; tmp>>=1)
        {
            if(tmp&1) b = b * a;
            a = a * a;      
        }
        printf("%d\n",b.a[r][0]%p);
    }
}
int main()
{
    runzhe2000::main();
}

BZOJ 4871 [Shoi2017]摧毁“树状图”

树形DP。

从来没写过这么长的DP方程……不过好在没有调特别久?

题意就是要在一棵树上找两条边不相交的链,使得去掉链后联通块数量最大。考虑可能的两条链的所有位置情况,总结一下,发现我们只需要维护一些形态的DP值(语文能力不好,怕口胡失败,特意画了一张图。图丑勿喷QAQ)。

状态对应图

分别记 f[i][0,1,2,3,4] 表示i的子树内,五种不同形态的链交,能在i子树能划分出的最多联通块个数。五种形态的链交分别是:

状态0:以根为一个端点的一条链
状态1:经过根的一条链(根可以也为其端点,即可以包含状态0)
状态2:不经过根的一条链
状态3:以根为一个端点的一条链+不经过根的一条链,且保证二者不相交
状态4:以根为一个端点的链或者2叉链或者3叉链(图示是2叉链,当然可以包含状态0)

有了这些DP值之后,考虑需要找的两条链的具体位置情况:

如果两条链不相交,分别设两个链上的最高点为链顶。两个链定的LCA要么是其中一个链顶,要么是一个其他点。如果是一个其他点则可以从状态1或2转移。否则分两种情况,要么两个链顶直接相连,要么中间隔了一段链。类似地可以用前缀后缀最值搞一搞。

如果相交,那链交一定是一个爪子一样的东西。那就只要用状态4和状态0搞一搞也就行了。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 500005
#define cmax(u,v) ((u)<(v)?(u)=(v):0)
using namespace std;
namespace runzhe2000
{
    int read()
    {
        int r = 0; char c = getchar();
        for(; c < '0' || c > '9'; c = getchar());
        for(; c >='0' && c <='9'; r = r*10+c-'0', c = getchar());
        return r;
    }
    int T, type, n, ecnt, last[N], f[N][5], val, _mxf0[N], pre_mxf0[N], suf_mxf0[N];
    struct edge{int next, to;}e[N<<1];
    void addedge(int a, int b){e[++ecnt] = (edge){last[a], b}; last[a] = ecnt;}

    void dp(int x, int fa)
    {
        int son_cnt = 0, mxf0_1 = 0, mxf0_2 = 0, mxf0_3 = 0, mxf0_4 = 0, mxf4 = 0;
        for(int i = last[x]; i; i = e[i].next) if(e[i].to != fa) son_cnt++, dp(e[i].to, x);
        for(int i = last[x], j = 1; i; i = e[i].next)
        {
            int y = e[i].to; if(y == fa) continue;
                 if(f[y][0] > mxf0_1) mxf0_4 = mxf0_3, mxf0_3 = mxf0_2, mxf0_2 = mxf0_1, mxf0_1 = f[y][0];
            else if(f[y][0] > mxf0_2) mxf0_4 = mxf0_3, mxf0_3 = mxf0_2, mxf0_2 = f[y][0];
            else if(f[y][0] > mxf0_3) mxf0_4 = mxf0_3, mxf0_3 = f[y][0];
            else if(f[y][0] > mxf0_4) mxf0_4 = f[y][0];
            cmax(f[x][2], f[y][2]);
            cmax(f[x][2], f[y][1] + 1);
            cmax(f[x][3], f[y][3]);
            cmax(mxf4, f[y][4]);
            _mxf0[j] = f[y][0];
            j++;
        }
        cmax(f[x][0], max(son_cnt, son_cnt-1+mxf0_1));
        cmax(f[x][1], max(f[x][0], son_cnt-2+mxf0_1+mxf0_2));
        f[x][3] += son_cnt - 1;
        cmax(f[x][4], mxf4 + son_cnt - 1);
        cmax(f[x][4], son_cnt - 3 + mxf0_1 + mxf0_2 + mxf0_3);
        cmax(f[x][4], son_cnt - 2 + mxf0_1 + mxf0_2);
        cmax(f[x][4], son_cnt - 1 + mxf0_1);
        int mxf0 = 0, mxf1 = 0, mxf2 = 0;
        for(int i = last[x]; i; i = e[i].next)
        {
            int y = e[i].to; if(y == fa) continue;
            cmax(f[x][3], son_cnt - 1 + max(f[y][1], f[y][2]));
            cmax(f[x][3], son_cnt - 2 + mxf0 + f[y][1]);
            cmax(f[x][3], son_cnt - 2 + mxf0 + f[y][2]);
            cmax(f[x][3], son_cnt - 2 + mxf1 + f[y][0]);
            cmax(f[x][3], son_cnt - 2 + mxf2 + f[y][0]);
            cmax(mxf0, f[y][0]); cmax(mxf1, f[y][1]); cmax(mxf2, f[y][2]); 
        }

        pre_mxf0[0] = suf_mxf0[son_cnt+1] = _mxf0[0] = _mxf0[son_cnt+1] = 0;
        for(int i = 1; i <= son_cnt; i++) pre_mxf0[i] = max(pre_mxf0[i-1], _mxf0[i]);
        for(int i = son_cnt; i >= 1; i--) suf_mxf0[i] = max(suf_mxf0[i+1], _mxf0[i]);

        int ans = 0;

        cmax(ans, mxf0_1 + mxf0_2 + mxf0_3 + mxf0_4 + son_cnt - 4);
        cmax(ans, mxf0_1 + mxf0_2 + mxf0_3 + son_cnt - 3);
        cmax(ans, mxf0_1 + mxf0_2 + son_cnt - 2);
        cmax(ans, mxf0_1 + son_cnt - 1);
        cmax(ans, f[x][1]); cmax(ans, f[x][3]); cmax(ans, f[x][4]);
        mxf0_1 = 0, mxf0_2 = 0, mxf1 = 0, mxf2 = 0, mxf4 = 0; int mxf3 = 0;
        for(int i = last[x], j = 1; i; i = e[i].next)
        {
            int y = e[i].to; if(y == fa) continue;

            //链顶不直接相连
            cmax(ans, f[y][3] + mxf0_1 + son_cnt - 2);
            cmax(ans, f[y][0] + mxf3 + son_cnt - 2);

            //链顶直接相连
            cmax(ans, max(f[y][1], f[y][2]) + mxf0_1 + mxf0_2 + son_cnt - 3); // right
            cmax(ans, f[y][0] + max(mxf1, mxf2) + suf_mxf0[j+1] + son_cnt - 3); // left
            cmax(ans, max(f[y][1], f[y][2]) + pre_mxf0[j-1] + suf_mxf0[j+1] + son_cnt - 3); // mid

            //互不为LCA
            cmax(ans, f[y][1] + mxf1 + 1 - (fa?1:0));
            cmax(ans, f[y][2] + mxf1 - (fa?1:0));
            cmax(ans, f[y][1] + mxf2 - (fa?1:0));
            cmax(ans, f[y][2] + mxf2 - 1 - (fa?1:0));

            //爪式
            cmax(ans, f[y][4] + mxf0_1 + son_cnt - 2);
            cmax(ans, f[y][0] + mxf4 + son_cnt - 2);


                 if(f[y][0] > mxf0_1) mxf0_2 = mxf0_1, mxf0_1 = f[y][0];
            else if(f[y][0] > mxf0_2) mxf0_2 = f[y][0];
            cmax(mxf1, f[y][1]);
            cmax(mxf2, f[y][2]);
            cmax(mxf3, f[y][3]);
            cmax(mxf4, f[y][4]);
            j++;
        }
        cmax(val, ans + (fa?1:0));
    }
    void main()
    {
        T = read(), type = read();
        for(; T--; )
        {
            n = read(); for(int i = 1; i <= type; i++) read(), read();
            for(int i = 1, x, y; i < n; i++) addedge(x = read(), y = read()), addedge(y, x);
            dp(1,0); printf("%d\n",val);
            ecnt = val = 0; for(int i = 1; i <= n; i++) 
            last[i] = f[i][0] = f[i][1] = f[i][2] = f[i][3] = f[i][4] = _mxf0[i] = pre_mxf0[i] = suf_mxf0[i] = 0;

        }
    }
}
int main()
{
    runzhe2000::main();
}

BZOJ 4872 [Shoi2017]分手是祝愿

高斯消元或推式子
先说高斯消元的做法:
记一个点按或者不按为1或0,这样就相当于一个异或方程组。要求这个方程每个变量总和最小的解,显然这是唯一的。而且要解它并不用高斯消元,只要从高位到低位一个一个确定即可。
考虑一个局面,假设这时候最小需要h步。接下来随机一个按一个开关。如果按到需要按的h个开关之一,则还要按h-1步,否则就还要按h+1步(证明:假设按h个开关之外的其它开关之后仍有h步的解,也就是说原来的局面还存在一个h+1的解。考虑异或方程,显然不可能即存在步数为h又存在步数为h+1的解,请自行脑补)。
也就是记f[i]表示当前局面要按i次,期望结束步数,从f[i-1],f[i+1]转移即可,这样是一个三元方程。可以列出一堆方程。考虑每个式子都只有三个变量,可以 O(n) 大力消元。
这样写完可以有95分,你要问我剩下的5分在哪?有一组数据也许是强行构造,使得消元的时候分母在模意义为0,也就是没有逆元,就炸了(出题人orz)
然后把整个方程组倒过来消就可以水过这个题。不过还是能卡就是了……
(推式子的做法详见这个代码下面)

#include<cstdio>
#include<algorithm>
#define N 100005
#define MOD 100003
using namespace std;
namespace runzhe2000
{
    typedef long long ll;
    int f[N][4], ans[N], n, k, need, a[N], b[N], fac;
    int fpow(int a, int b)
    {
        int r = 1;
        for(; b; b>>=1)
        {
            if(b&1) r = (ll)r*a%MOD;
            a = (ll)a*a%MOD;
        }
        return r;
    }
    void main()
    {
        scanf("%d%d",&n,&k); fac = 1;
        for(int i = 1; i <= n; i++) scanf("%d",&a[i]), fac = (ll)fac * i % MOD;
        for(int i = n; i >= 1; i--)
        {
            b[i] = a[i];
            for(int j = i+i; j <= n; j += i) b[i] ^= b[j];
            need += b[i];
        }
        if(need <= k)printf("%lld\n",(ll)need * fac % MOD);
        else
        {
            f[k+1][0] = 0; f[k+1][1] = -1; f[k+1][2] = (ll)(n-k-1)*fpow(n,MOD-2)%MOD; f[k+1][3] = (-1-(ll)(k+1)*k*fpow(n,MOD-2)%MOD)%MOD;
            for(int i = k+2; i <= n; i++)
                f[i][0] = (ll)i*fpow(n,MOD-2)%MOD, f[i][1] = -1, f[i][2] = (ll)(n-i)*fpow(n,MOD-2)%MOD, f[i][3] = -1;
            /*
            for(int i = k+1; i < n; i++)
            {
                ll base = (ll)f[i+1][0] * fpow(f[i][1], MOD-2) % MOD;
                (f[i+1][0] -= f[i][1] * base % MOD) %= MOD;
                (f[i+1][1] -= f[i][2] * base % MOD) %= MOD;
                (f[i+1][3] -= f[i][3] * base % MOD) %= MOD;
            }
            for(int i = n; i >= k+1; i--)
            {
                ans[i] = (ll) f[i][3] * fpow(f[i][1], MOD-2) % MOD;
                (f[i-1][3] -= (ll)ans[i] * f[i-1][2] % MOD) %= MOD;
            }
            */
            for(int i = n; i > k+1; i--)
            {
                ll base = (ll)f[i-1][2] * fpow(f[i][1], MOD-2) % MOD;
                (f[i-1][2] -= f[i][1] * base % MOD) %= MOD;
                (f[i-1][1] -= f[i][0] * base % MOD) %= MOD;
                (f[i-1][3] -= f[i][3] * base % MOD) %= MOD;
            }
            for(int i = k+1; i <= n; i++)
            {
                ans[i] = (ll) f[i][3] * fpow(f[i][1], MOD-2) % MOD;
                (f[i+1][3] -= (ll)ans[i] * f[i+1][0] % MOD) %= MOD;
            }

            printf("%lld\n",((ll)ans[need] * fac % MOD + MOD)%MOD);
        }
    }
}
int main()
{
    runzhe2000::main();
}

我们把高斯消元里的每一次消元都手动展开,加上边界情况,联立起来瞎几把推一下就有:
f[ni]=f[ni1](ni1)!i!ik=0Ckn(ni)!
然后我调了半天没调出来,近乎崩溃。
后来弱弱地回想起来答案要乘一个阶乘…好气啊

#include<cstdio>
#include<algorithm>
#define N 100005
#define MOD 100003
using namespace std;
namespace runzhe2000
{
    typedef long long ll;
    int n, k, need, a[N], b[N], fac[N], inv[N], inv_fac[N];
    int C(int a, int b){return (ll)fac[a] * inv_fac[b] % MOD * inv_fac[a-b] % MOD;}
    void main()
    {
        scanf("%d%d",&n,&k); fac[0] = inv[1] = inv_fac[0] = 1;
        for(int i = 1; i <= n; i++) 
        {
            scanf("%d",&a[i]), fac[i] = (ll)fac[i-1] * i % MOD;
            if(i>1)inv[i] = (ll)(MOD-MOD/i)*inv[MOD%i] % MOD;
            inv_fac[i] = (ll)inv_fac[i-1] * inv[i] % MOD;
        }
        for(int i = n; i >= 1; i--)
        {
            b[i] = a[i];
            for(int j = i+i; j <= n; j += i) b[i] ^= b[j];
            need += b[i];
        }
        if(need <= k)printf("%lld\n",(ll)need * fac[n] % MOD);
        else
        {
            int cur = k, base = 0;
            for(int j = 0; j <= n-k; j++) (base += C(n, j)) %= MOD;
            for(int i = n-k-1, ii = n-need; i >= ii; i--)
            {
                int tmp = (ll) (base -= C(n, i+1)) * fac[i] % MOD * inv_fac[n-1] % MOD * fac[n-i-1] % MOD;;
                (cur += tmp) %= MOD; base %= MOD;
            }
            cur = (ll) cur * fac[n] % MOD;
            printf("%d\n",(cur+MOD)%MOD);
        }
    }
}
int main()
{
    runzhe2000::main();
}

BZOJ 4873 [Shoi2017]寿司餐厅

最大权闭合子图
这题有给m=0的部分分,考虑每一次选择的区间都不会相交。因此暴力DP一个即可。
然后我就掉进了DP的坑里好久……考虑怎么DP都没办法记录哪些编号已选,那就不能DP。这题的限制都是贡献只有一次,考虑流。一个大区间选了就必选小区间,这就是最大权闭合子图的模型……然后上最小割即可。

#include<cstdio>
#include<queue>
#include<cstring>
#define N 105
#define M 55555
using namespace std;
namespace runzhe2000
{
    const int INF = 1<<29;
    int n, m, a[N], d[N][N], id[N][N], ecnt = 1, tot, s, t, last[M], cur[M], spe[M], level[M];
    struct edge{int next, to, flow;}e[M<<1];
    void addedge(int a, int b, int c)
    {
        e[++ecnt] = (edge){last[a], b, c};
        last[a] = ecnt;
        e[++ecnt] = (edge){last[b], a, 0};
        last[b] = ecnt;
    }
    int dfs(int x, int flow)
    {
        if(x == t) return flow; int use = 0;
        for(int &i = cur[x]; i; i = e[i].next)
        {
            int y = e[i].to; if(level[y] != level[x] + 1) continue;
            int w = dfs(y, min(flow-use, e[i].flow));
            e[i].flow -= w; e[i^1].flow += w;
            use += w; if(use == flow) return use;
        }
        return use;
    }
    bool bfs()
    {
        queue<int> q; q.push(s); memset(level,0,sizeof(level)); level[s] = 1;
        for(; !q.empty(); )
        {
            int x = q.front(); q.pop();
            for(int i = last[x]; i; i = e[i].next) if(e[i].flow)
            {
                int y = e[i].to;
                if(!level[y]) 
                {
                    level[y] = level[x] + 1, q.push(y);
                    if(y == t) return 1;
                }
            }
        }
        return 0;
    }
    int dinic(){int r = 0; for(; bfs();) memcpy(cur, last, sizeof(cur)), r += dfs(s, INF); return r;}
    void main()
    {
        scanf("%d%d",&n,&m); s = ++tot, t = ++tot;
        for(int i = 1; i <= n; i++) scanf("%d",&a[i]);
        for(int i = 1; i <= n; i++) for(int j = i; j <= n; j++) 
        {
            scanf("%d",&d[i][j]); id[i][j] = ++tot;
            d[i][j] > 0 ? addedge(s, id[i][j], d[i][j]) : addedge(id[i][j], t, -d[i][j]);
        }
        for(int i = 1; i <= n; i++) 
        {
            for(int j = i+1; j <= n; j++) 
            {
                addedge(id[i][j], id[i][j-1], INF);
                addedge(id[i][j], id[i+1][j], INF);
            }
            int pos = ++tot;
            addedge(id[i][i], pos, INF);
            addedge(pos, t, a[i]);
            if(m)
            {
                int pos2 = spe[a[i]] ? spe[a[i]] : spe[a[i]] = ++tot;
                addedge(pos, pos2, INF);
                if(pos2 == tot)addedge(pos2, t, a[i]*a[i]);
            }
        }
        int r = 0; for(int i = last[s]; i; i = e[i].next) r += e[i].flow;
        printf("%d\n",r-dinic());
    }
}
int main()
{
    runzhe2000::main();
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值