HDU 2020 多校第二场 游记

咕了整整一天才来写 2333

这次邓老师没来 /kk,不过打的还算不错,可惜还是有一题没调出来,不然就是 11 题队了 /ll

1001

题意好像有点奇怪,不过 djq 还是一眼看对了,拿了这题一血。

题解

看对题就没啥好说的了,裸签到题,把点按权值从大到小排序,不断加入点,用并查集维护当前连通块个数,乘一下相邻两个权值的差加起来即可。复杂度 O ( n log n ) O(n \log n)

code by djq

#include <bits/stdc++.h>
#define rep(i, n) for(int i = 0; i < (int)(n); i ++)
#define rep1(i, n) for(int i = 1; i <= (int)(n); i ++)
#define MP make_pair

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MOD = 998244353;

int n, m, b[100005];
PII ver[100005];
int rt[100005];

int root(int x)
{
    if(rt[x] == x) return x;
    return rt[x] = root(rt[x]);
}

bool unite(int u, int v)
{
    u = root(u); v = root(v);
    if(u == v) return false;
    rt[v] = u;
    return true;
}

vector<int> G[100005];

void solve()
{
    scanf("%d%d", &n, &m);
    rep1(i, n) scanf("%d", &b[i]);
    rep1(i, n) ver[i] = MP(b[i], i);
    sort(ver + 1, ver + 1 + n);
    reverse(ver + 1, ver + 1 + n);
    ver[n + 1].first = 0;
    
    rep1(i, n) G[i].clear();
    rep(i, m) {
        int u, v;
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    
    rep1(i, n) rt[i] = i;
    
    int cnt = 0;
    LL ans = 0;
    rep1(i, n) {
        cnt ++;
        int v = ver[i].second;
        rep(j, G[v].size()) {
            int u = G[v][j];
            if(b[u] >= b[v] && unite(u, v)) cnt --;
        }
        ans += 1LL * (ver[i].first - ver[i + 1].first) * cnt;
    }
    printf("%lld\n", ans);
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T --) solve();
    return 0;
}

1002

其实应该也算个比较套路的题?

考虑拆掉绝对值之后每个点的贡献是 -2,0,2,如果在首尾的话比较特别,是 -1 或 1.

由于 a i a_i 互不相同,大概按照十三练里波浪那题 dp 就差不多了。

f ( i , j , 0 / 1 / 2 , k ) f(i,j,0/1/2,k) 表示当前从小到大加到了第 i i 个数,有 j j 个连续段,其中首尾放了 0/1/2 个数时的第 k k 大。

转移的话就枚举当前的几种情况:产生新的段;在原来段的开头或结尾加入一个(即不改变段数);合并两段。以及还要考虑当前数字是否放在开头/结尾。

合并前 k k 大,大概写个类似归并的东西就可以了。复杂度 O ( n 2 k ) O(n^2k)

(转移还是稍微有些复杂的)

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

const int MAXN = 605, MOD = 1e9 + 7;
struct Data { int v, c; } f[2][MAXN][3][25];
int T, n, K, aa[MAXN], len[2][MAXN][3];
void calc(Data *a, const Data *b, int &na, int nb, int val, LL coef) {
    if (nb == 0) return;
    if (na == 0) {
        na = nb;
        for (int i = 1; i <= na; i++)
            a[i] = Data { b[i].v + val, b[i].c * coef % MOD };
        return;
    }
    static Data d[25];
    int i = 1, j = 1, c = 0;
    while (c < K && (i <= na || j <= nb)) {
        if (j > nb || (i <= na && a[i].v > b[j].v + val))
            d[++c] = a[i], ++i;
        else if (i > na || (j <= nb && b[j].v + val > a[i].v))
            d[++c] = Data { b[j].v + val, b[j].c * coef % MOD }, ++j;
        else d[++c] = Data { a[i].v, (a[i].c + b[j].c * coef) % MOD }, ++i, ++j;
    }
    for (int i = 1; i <= c; i++) {
        a[i] = d[i];
        if (i < c && d[i].v == d[i + 1].v) {
            puts("fuck");
        }
    }
    na = c;
}

int main() {
    for (scanf("%d", &T); T--;) {
        scanf("%d%d", &n, &K);
        for (int i = 1; i <= n; i++)
            scanf("%d", aa + i);
        sort(aa + 1, aa + 1 + n);
        memset(len, 0, sizeof(len));
        f[0][0][0][1] = Data { 0, 1 };
        len[0][0][0] = 1;
        for (int i = 1; i <= n; i++) {
            int a = i & 1, b = !a;
            memset(len[a], 0, sizeof(len[a]));
            for (int j = 1; j <= i; j++) {
                calc(f[a][j][0], f[b][j - 1][0], len[a][j][0], len[b][j - 1][0], -2 * aa[i], j);
                calc(f[a][j][0], f[b][j][0], len[a][j][0], len[b][j][0], 0, 2 * j);
                calc(f[a][j][0], f[b][j + 1][0], len[a][j][0], len[b][j + 1][0], 2 * aa[i], j);
                
                calc(f[a][j][1], f[b][j - 1][1], len[a][j][1], len[b][j - 1][1], -2 * aa[i], j - 1);
                calc(f[a][j][1], f[b][j][1], len[a][j][1], len[b][j][1], 0, 2 * j - 1);
                calc(f[a][j][1], f[b][j + 1][1], len[a][j][1], len[b][j + 1][1], 2 * aa[i], j);
                
                calc(f[a][j][1], f[b][j - 1][0], len[a][j][1], len[b][j - 1][0], -aa[i], 2);
                calc(f[a][j][1], f[b][j][0], len[a][j][1], len[b][j][0], aa[i], 2);
                
                calc(f[a][j][2], f[b][j - 1][2], len[a][j][2], len[b][j - 1][2], -2 * aa[i], j - 2);
                calc(f[a][j][2], f[b][j][2], len[a][j][2], len[b][j][2], 0, 2 * j - 2);
                calc(f[a][j][2], f[b][j + 1][2], len[a][j][2], len[b][j + 1][2], 2 * aa[i], j);
                
                calc(f[a][j][2], f[b][j - 1][1], len[a][j][2], len[b][j - 1][1], -aa[i], 1);
                calc(f[a][j][2], f[b][j][1], len[a][j][2], len[b][j][1], aa[i], 1);
            }
        }
        for (int i = 1; i <= K; i++) {
            const Data &d = f[n & 1][1][2][i];
            if (d.c) printf("%d %d\n", d.v, d.c);
            else puts("-1");
        }
    }
    return 0;
}

1003

这场比赛里最神的一道题了吧,好像很多人都觉得 q n log n q\sqrt{n \log n} 能过然而被卡了 2333.

神仙随机化题,考虑随机 k k [ 0 , 1 ] [0,1] 中的实数,最小值的期望是 1 k + 1 \frac{1}{k+1} 。于是考虑给每种颜色随机一个权值,然后求链上权值最小值就可以近似的得到颜色个数。我们多随机几次,取这些最小值的平均值进行比较即可。

于是可以直接树剖维护(倍增空间大预处理复杂度大过不了 qwq),也可以全局平衡二叉树维护(不过好像这题不是查询到根的链,可能难写一些)。

懒得写了,直接贴个题解代码吧。

#include<cstdio>
#include<algorithm>
#pragma GCC optimize(2)
using namespace std;
typedef unsigned int U;
typedef long long ll;
const int N=500010,K=30;
const U inf=~0U;
int Case,n,m,i,j,last,op,x,y,A,B,C,D,col[N],g[N],v[N<<1],nxt[N<<1],ed;
int f[N],d[N],size[N],son[N],top[N],loc[N],q[N],dfn;
U w[N][K],val[1111111][K],tmp[K];
U SX=335634763,SY=873658265,SZ=192849106,SW=746126501;
inline U xorshift128(){
  U t=SX^(SX<<11);
  SX=SY;
  SY=SZ;
  SZ=SW;
  return SW=SW^(SW>>19)^t^(t>>8);
}
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs1(int x,int y){
  f[x]=y,d[x]=d[y]+1,size[x]=1;
  for(int i=g[x];i;i=nxt[i]){
    int u=v[i];
    if(u==y)continue;
    dfs1(u,x);
    size[x]+=size[u];
    if(size[u]>size[son[x]])son[x]=u;
  }
}
void dfs2(int x,int y){
  q[loc[x]=++dfn]=x;
  top[x]=y;
  if(son[x])dfs2(son[x],y);
  for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]&&v[i]!=son[x])dfs2(v[i],v[i]);
}
inline void up(int x){for(int i=0;i<K;i++)val[x][i]=min(val[x<<1][i],val[x<<1|1][i]);}
void build(int x,int a,int b){
  if(a==b){
    int o=col[q[a]];
    for(int i=0;i<K;i++)val[x][i]=w[o][i];
    return;
  }
  int mid=(a+b)>>1;
  build(x<<1,a,mid),build(x<<1|1,mid+1,b);
  up(x);
}
void change(int x,int a,int b,int c,int p){
  if(a==b){
    for(int i=0;i<K;i++)val[x][i]=w[p][i];
    return;
  }
  int mid=(a+b)>>1;
  if(c<=mid)change(x<<1,a,mid,c,p);else change(x<<1|1,mid+1,b,c,p);
  up(x);
}
inline void umin(U&a,U b){a>b?(a=b):0;}
void ask(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d){
    for(int i=0;i<K;i++)umin(tmp[i],val[x][i]);
    return;
  }
  int mid=(a+b)>>1;
  if(c<=mid)ask(x<<1,a,mid,c,d);
  if(d>mid)ask(x<<1|1,mid+1,b,c,d);
}
inline ll estimate(int x,int y){
  for(int i=0;i<K;i++)tmp[i]=inf;
  for(;top[x]!=top[y];x=f[top[x]]){
    if(d[top[x]]<d[top[y]])swap(x,y);
    ask(1,1,n,loc[top[x]],loc[x]);
  }
  if(d[x]<d[y])swap(x,y);
  ask(1,1,n,loc[y],loc[x]);
  ll ret=0;
  for(int i=0;i<K;i++)ret+=tmp[i];
  return ret;
}
int main(){
  for(i=1;i<N;i++)for(j=0;j<K;j++)w[i][j]=xorshift128();
  scanf("%d",&Case);
  while(Case--){
    scanf("%d%d",&n,&m);
    for(ed=dfn=last=i=0;i<=n;i++)g[i]=f[i]=d[i]=size[i]=son[i]=0;
    for(i=1;i<=n;i++)scanf("%d",&col[i]);
    for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dfs1(1,0);
    dfs2(1,1);
    build(1,1,n);
    while(m--){
      scanf("%d%d%d",&op,&A,&B);
      A^=last,B^=last;
      if(op==1)change(1,1,n,loc[A],B);
      else{
        scanf("%d%d",&C,&D);
        C^=last,D^=last;
        ll E=estimate(A,B),F=estimate(C,D);
        if(E<F){
          puts("Yes");
          last++;
        }else puts("No");
      }
    }
  }
}

1004

本来以为 O ( n 3 ) O(n^3) 能过的,后来为了求稳还是写了发主席树……

考虑当前限制了 x 1 , y 1 , x 2 , y 2 x_1,y_1,x_2,y_2 这个矩形,那么最终的路径要么经过 ( x 1 1 , y 2 + 1 ) (x_1-1,y2+1) 这个格子或其上面的某个鸽子,要么经过 ( x 2 + 1 , y 1 1 ) (x2+1,y1-1) 或其左边的某个格子。

注意到权值定义为 ( n 2 ) a i , j (n^2)^{a_{i,j}} ,也就是说能选大的就尽量选大的。我们考虑 dp, f ( i , j ) f(i,j) 表示 ( 1 , 1 ) (1,1) ( i , j ) (i,j) 能得到的最大值, g ( i , j ) g(i,j) 表示 ( i , j ) (i,j) ( n , n ) (n,n) 的最大值,于是现在的问题就落到了比较上。

我们用可持久化线段树维护每种数字的出现次数,比较大小的话就直接线段树二分,每个节点维护下面次数的 hash 值和答案(刚好答案也可以作为第二种 hash)。

接下来要算一下对于 ( x , y ) (x,y) ,强制经过其上方的格子能获得的最大值。首先要么是 ( x 1 , y ) (x-1,y) 的答案,要么是强制经过 ( x , y ) (x,y) 的答案。这个时候我们发现需要比较两个主席树加起来,和另外两个主席树加起来的大小(因为要把 f ( x , y ) + g ( x , y ) f(x,y)+g(x,y) 作为 ( x , y ) (x,y) 的权值)。hash 只要是正常的字符串 hash,就可以直接加,因此还是可以线段树二分的。复杂度 O ( n 2 log n + q log n ) O(n^2 \log n + q \log n)

自己写的代码找不到了,下面是 code by djq。

#include <bits/stdc++.h>
#define rep(i, n) for(int i = 0; i < (int)(n); i ++)
#define rep1(i, n) for(int i = 1; i <= (int)(n); i ++)
#define MP make_pair

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
int D0 = 243, D1;
int MOD0 = 998244353, MOD1 = 1e9 + 7;
int PW0[200005], PW1[200005];

void init_pw()
{
    PW0[0] = PW1[0] = 1;
    rep1(i, 200000) {
        PW0[i] = 1LL * PW0[i - 1] * D0 % MOD0;
        PW1[i] = 1LL * PW1[i - 1] * D1 % MOD1;
    }
}

namespace segt
{
    int cnt[8000005], hs0[8000005], hs1[8000005], lson[8000005], rson[8000005], tot;
    
    void init()
    {
        memset(hs0, 0, sizeof(hs0));
        memset(hs1, 0, sizeof(hs1));
        memset(lson, -1, sizeof(lson));
        memset(rson, -1, sizeof(rson));
        tot = 0;
    }
    
    void pushup(int cur)
    {
        hs0[cur] = ((lson[cur] == -1 ? 0 : hs0[lson[cur]]) + (rson[cur] == -1 ? 0 : hs0[rson[cur]])) % MOD0;
        hs1[cur] = ((lson[cur] == -1 ? 0 : hs1[lson[cur]]) + (rson[cur] == -1 ? 0 : hs1[rson[cur]])) % MOD1;
    }
    
    int modify(int cur, int dat, int cl, int cr)
    {
        int ret = tot ++;
        if(cl == cr) {
            cnt[ret] = (cur == -1 ? 0 : cnt[cur]) + 1;
            hs0[ret] = 1LL * PW0[dat] * cnt[ret] % MOD0;
            hs1[ret] = 1LL * PW1[dat] * cnt[ret] % MOD1;
            return ret;
        }
        int mid = cl + cr >> 1;
        if(dat <= mid) {
            lson[ret] = modify(cur == -1 ? -1 : lson[cur], dat, cl, mid);
            rson[ret] = cur == -1 ? -1 : rson[cur];
        } else {
            lson[ret] = cur == -1 ? -1 : lson[cur];
            rson[ret] = modify(cur == -1 ? -1 : rson[cur], dat, mid + 1, cr);
        }
        pushup(ret);
        return ret;
    }
    
    int comp(int a, int b, int cl, int cr)
    {
        if(a == -1 && b == -1) return 0;
        if(a == -1) return 1;
        if(b == -1) return -1;
        if(hs0[a] == hs0[b] && hs1[a] == hs1[b]) return 0;
        if(cl == cr) return cnt[a] < cnt[b] ? 1 : -1;
        int mid = cl + cr >> 1;
        int ret = comp(rson[a], rson[b], mid + 1, cr);
        if(ret == 0) ret = comp(lson[a], lson[b], cl, mid);
        return ret;
    }
    
    int comp2(int a0, int a1, int b0, int b1, int cl, int cr)
    {
        int ah0 = ((a0 == -1 ? 0 : hs0[a0]) + (a1 == -1 ? 0 : hs0[a1])) % MOD0;
        int ah1 = ((a0 == -1 ? 0 : hs1[a0]) + (a1 == -1 ? 0 : hs1[a1])) % MOD1;
        int bh0 = ((b0 == -1 ? 0 : hs0[b0]) + (b1 == -1 ? 0 : hs0[b1])) % MOD0;
        int bh1 = ((b0 == -1 ? 0 : hs1[b0]) + (b1 == -1 ? 0 : hs1[b1])) % MOD1;
        if(ah0 == bh0 && ah1 == bh1) return 0;
        if(cl == cr) return (a0 == -1 ? 0 : cnt[a0]) + (a1 == -1 ? 0 : cnt[a1]) <
        (b0 == -1 ? 0 : cnt[b0]) + (b1 == -1 ? 0 : cnt[b1]) ? 1 : -1;
        int mid = cl + cr >> 1;
        int ret = comp2(a0 == -1 ? -1 : rson[a0], a1 == -1 ? -1 : rson[a1],
        b0 == -1 ? -1 : rson[b0], b1 == -1 ? -1 : rson[b1], mid + 1, cr);
        if(ret == 0) ret = comp2(a0 == -1 ? -1 : lson[a0], a1 == -1 ? -1 : lson[a1],
        b0 == -1 ? -1 : lson[b0], b1 == -1 ? -1 : lson[b1], cl, mid);
        return ret;
    }
}

int n, q, a[405][405];
int dp0[405][405], f1[405][405], dp1[405][405], lpos[405][405], upos[405][405];

void solve()
{
    scanf("%d%d", &n, &q);
    rep1(i, n) rep1(j, n) scanf("%d", &a[i][j]);
    D1 = n * n;
    init_pw();
    segt::init();
    
    memset(dp0, -1, sizeof(dp0));
    memset(f1, -1, sizeof(f1));
    memset(dp1, -1, sizeof(dp1));
    memset(lpos, 0, sizeof(lpos));
    memset(upos, 0, sizeof(upos));
    
    rep1(i, n) rep1(j, n) {
        dp0[i][j] = segt::comp(dp0[i - 1][j], dp0[i][j - 1], 0, 262143) == -1 ? dp0[i - 1][j] : dp0[i][j - 1];
        dp0[i][j] = segt::modify(dp0[i][j], a[i][j], 0, 262143);
    }
    
    for(int i = n; i >= 1; i --) for(int j = n; j >= 1; j --) {
        f1[i][j] = segt::comp(dp1[i + 1][j], dp1[i][j + 1], 0, 262143) == -1 ? dp1[i + 1][j] : dp1[i][j + 1];
        dp1[i][j] = segt::modify(f1[i][j], a[i][j], 0, 262143);
    }
    
    rep1(i, n) rep1(j, n) {
        int lj = lpos[i][j - 1];
        lpos[i][j] = segt::comp2(dp0[i][lj], f1[i][lj], dp0[i][j], f1[i][j], 0, 262143) == -1 ? lj : j;
    }
    
    rep1(i, n) rep1(j, n) {
        int ui = upos[i - 1][j];
        upos[i][j] = segt::comp2(dp0[ui][j], f1[ui][j], dp0[i][j], f1[i][j], 0, 262143) == -1 ? ui : i;
    }
    
    rep(i, q) {
        int lx, rx, ly, ry;
        scanf("%d%d%d%d", &lx, &rx, &ly, &ry);
        int i0 = rx + 1, j0 = ly - 1, i1 = lx - 1, j1 = ry + 1;
        j0 = lpos[i0][j0];
        i1 = upos[i1][j1];
        int ret = segt::comp2(dp0[i0][j0], f1[i0][j0], dp0[i1][j1], f1[i1][j1], 0, 262143) == -1 ?
        (segt::hs1[dp0[i0][j0]] + segt::hs1[f1[i0][j0]]) % MOD1 : (segt::hs1[dp0[i1][j1]] + segt::hs1[f1[i1][j1]]) % MOD1;
        printf("%d\n", ret);
    }
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T --) solve();
    return 0;
}

1005

这个题还是比较 easy 的,考虑对于一个工人,找到他最小值的所在位置扔到一个 vector 里(为了不出错把最小值左右两个整数都加入)。

接下来考虑对于 vector 里的每个元素,找到第一个没有在 set 里出现过的大于它的数字 k k ,把 k k 扔到 set 里。

同理的,对于 vector 里的每个元素,找到第一个没有在 set 里出现过的小于它的数字 k k ,把 k k 扔到 set 里。

然后对于 set 里的元素,和工人两两连边,跑 n n 遍费用流即可。不难发现最优解肯定能够被包含在上面的构造方法中,因此点数 O ( n ) O(n) ,边数 O ( n 2 ) O(n^2) ,肯定能过。

code by djq

#include <bits/stdc++.h>
#define rep(i, n) for(int i = 0; i < (int)(n); i ++)
#define rep1(i, n) for(int i = 1; i <= (int)(n); i ++)
#define MP make_pair

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const LL INF = 1.3e17;


namespace flow
{
    const int MAXN = 400;
    struct edge
    {
        int to, cap;
        LL cost;
    };
    int n, m;
    edge ed[30005];
    vector<int> G[405];
    
    void clear()
    {
        m = 0;
        rep(i, n) G[i].clear();
    }
    
    void add_edge(int u, int v, int fl, LL co)
    {
        ed[m].to = v; ed[m].cap = fl; ed[m].cost = co; G[u].push_back(m); m ++;
        ed[m].to = u; ed[m].cap = 0; ed[m].cost = -co; G[v].push_back(m); m ++;
    }
    
    LL h[405];
    LL pat[405];
    int prev[405];
    bool vis[405];
    
    bool dijk(int s, int t)
    {
        rep(i, n) {
            pat[i] = INF;
            vis[i] = false;
        }
        pat[s] = 0;
        priority_queue<pair<LL, int> > que;
        que.push(MP(0, s));
        
        while(!que.empty()) {
            int cur = que.top().second;
            que.pop();
            if(vis[cur]) continue;
            vis[cur] = true;
            rep(i, G[cur].size()) {
                int ce = G[cur][i];
                if(ed[ce].cap == 0) continue;
                if(pat[ed[ce].to] > pat[cur] + ed[ce].cost) {
                    pat[ed[ce].to] = pat[cur] + ed[ce].cost;
                    prev[ed[ce].to] = ce;
                    que.push(MP(-pat[ed[ce].to], ed[ce].to));
                }
            }
        }
        
        return pat[t] != INF;
    }
    
    void mcf(int s, int t, LL* ret)
    {
        rep(i, n) h[i] = 0;
        LL ans = 0;
        while(dijk(s, t)) {
            //in this occasion flow must be 1
            int cur = t, cf = 1;
            while(cur != s) {
                ed[prev[cur]].cap -= cf;
                ed[prev[cur] ^ 1].cap += cf;
                cur = ed[prev[cur] ^ 1].to;
            }
            rep(i, m) ed[i].cost += pat[ed[i ^ 1].to] - pat[ed[i].to];
            rep(i, n) h[i] += pat[i];
            ans += h[t] * cf;
            *(++ ret) = ans;
        }
    }
}

int n, m;
LL a[55], b[55], c[55];
set<LL> S;
vector<LL> hv, nhv; 
LL ans[55];

void solve()
{
    scanf("%d%d", &n, &m);
    rep(i, n) scanf("%lld%lld%lld", &a[i], &b[i], &c[i]);
    
    hv.clear();
    nhv.clear();
    rep(i, n) {
        if(b[i] > 0) hv.push_back(1);
        else {
            LL pt = max(min((-b[i]) / 2 / a[i], m - 1LL), 1LL);
            if(pt >= 1 && pt <= m) hv.push_back(pt);
            if(pt + 1 >= 1 && pt + 1 <= m) hv.push_back(pt + 1); 
        }
    }
    
    S.clear();
    rep(i, hv.size()) {
        int cur = hv[i];
        while(S.find(cur) != S.end()) cur ++;
        if(cur < 1 || cur > m) continue;
        nhv.push_back(cur);
        S.insert(cur);
    }
    S.clear();
    rep(i, hv.size()) {
        int cur = hv[i];
        while(S.find(cur) != S.end()) cur --;
        if(cur < 1 || cur > m) continue;
        nhv.push_back(cur);
        S.insert(cur);
    }
    sort(nhv.begin(), nhv.end());
    nhv.resize(unique(nhv.begin(), nhv.end()) - nhv.begin());
    
    flow::n = n + nhv.size() + 2;
    flow::clear();
    rep(i, n) flow::add_edge(flow::n - 2, i, 1, 0);
    rep(i, nhv.size()) flow::add_edge(i + n, flow::n - 1, 1, 0);
    rep(i, n) rep(j, nhv.size())
    flow::add_edge(i, j + n, 1, a[i] * nhv[j] * nhv[j] + b[i] * nhv[j] + c[i]);
    
    flow::mcf(flow::n - 2, flow::n - 1, ans);
    rep1(i, n) printf("%lld%c", ans[i], " \n"[i == n]);
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T --) solve();
    return 0;
}

1006

刚开始以为是那个 PA 原题,吓懵了……仔细一看发现原来只需要判断就行。那就是签到题了,随便取几个质数膜一下判相不相等即可。复杂度 O ( n ) O(n)

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> void chkmax(T &a, const T &b) { a = a < b ? b : a; }
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }

const int MAXN = 2000005, B = 5;
const int MOD[B] = { 1000000007, 1000000009, 1000000021, 1000000033, 1000000087 };
int fib[MAXN][5], arr[B], brr[B], crr[B], T, na, nb, nc;
bool cc[MAXN];
void init() {
    int n = 2e6 + 3;
    for (int i = 0; i < B; i++)
        fib[0][i] = 1, fib[1][i] = 1;
    for (int i = 2; i <= n; i++)
    for (int j = 0; j < B; j++)
        fib[i][j] = (fib[i - 1][j] + fib[i - 2][j]) % MOD[j];
}

int main() {
    init();
    for (scanf("%d", &T); T--;) {
        scanf("%d", &na);
        memset(arr, 0, sizeof(arr));
        memset(brr, 0, sizeof(brr));
        memset(crr, 0, sizeof(crr));
        for (int i = 1; i <= na; i++) {
            int t; scanf("%d", &t);
            if (t) for (int j = 0; j < B; j++)
                arr[j] = (arr[j] + fib[i][j]) % MOD[j];
        }
        scanf("%d", &nb);
        for (int i = 1; i <= nb; i++) {
            int t; scanf("%d", &t);
            if (t) for (int j = 0; j < B; j++)
                brr[j] = (brr[j] + fib[i][j]) % MOD[j];
        }
        scanf("%d", &nc);
        for (int i = 1; i <= nc; i++) {
            int t; scanf("%d", &t);
            cc[i] = t;
            if (t) for (int j = 0; j < B; j++)
                crr[j] = (crr[j] + fib[i][j]) % MOD[j];
        }
        for (int i = 0; i < B; i++)
            arr[i] = (LL)arr[i] * brr[i] % MOD[i];
        for (int i = 1; i <= nc; i++) if (!cc[i]) {
            if (cc[i - 1] || cc[i + 1]) continue;
            bool flag = true;
            for (int j = 0; j < B; j++)
                if ((crr[j] + fib[i][j]) % MOD[j] != arr[j])
                    { flag = false; break; }
            if (flag) {
                printf("%d\n", i);
                break;
            }
        }
    }
    return 0;
}

1007

要求直径最小,可以二分答案,然后记 f ( i , j ) f(i,j) 表示 i i 的子树,有 j j 条边选了权值 a a i i 子树的深度最小是多少。转移的话枚举当前儿子选了多少个 a a ,如果当前儿子和之前的儿子形成的路径已经 > m i d >mid 了就不能转移。最后看有没有合法方案即可。复杂度 O ( n k log 1 0 14 ) O(nk \log 10^{14})

code by lqs

#include<bits/stdc++.h>
using namespace std;
int test,n,k,x,y,a,b;
long long l,r,mid,dp[22222][22],f[22],gg[22],nf[22];
struct edge
{
    int to,a,b;
};
vector<edge> g[22222];
void dfs(int i,int fa)
{
    for (int j=0;j<g[i].size();j++)
    {
        int to=g[i][j].to;
        if (to==fa) continue;
        dfs(to,i);
    }
    for (int j=0;j<=k;j++) f[j]=1e18;
    f[0]=0;
    for (int j=0;j<g[i].size();j++)
    {
        int to=g[i][j].to;
        if (to==fa) continue;
        gg[0]=dp[to][0]+g[i][j].b;
        for (int h=1;h<=k;h++)
        {
            gg[h]=min(dp[to][h]+g[i][j].b,dp[to][h-1]+g[i][j].a);
        }
        for (int h=0;h<=k;h++) nf[h]=1e18;
        for (int h=0;h<=k;h++)
        {
            for (int p=0;p+h<=k;p++)
            {
                if (f[h]+gg[p]<=mid) nf[h+p]=min(nf[h+p],max(f[h],gg[p]));
            }
        }
        for (int h=0;h<=k;h++) f[h]=nf[h];
    }
    for (int j=0;j<=k;j++) dp[i][j]=f[j];
}
bool check()
{
    dfs(1,0);
    return (dp[1][k]<=mid);
}
int main()
{
    scanf("%d",&test);
    while(test--)
    {
        scanf("%d%d",&n,&k);
        for (int i=1;i<=n;i++) g[i].clear();
        for (int i=1;i<n;i++)
        {
            scanf("%d%d%d%d",&x,&y,&a,&b);
            g[x].push_back((edge){y,a,b});
            g[y].push_back((edge){x,a,b});
        }
        l=0;r=1e14;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if (check()) r=mid-1;
            else l=mid+1;
        }
        printf("%lld\n",l);
    }
    return 0;
}

1008

假设没有加入删除操作,我们考虑把这些函数按照 a i a_i 排序,接下来从左向右扫维护一个栈,假设当前扫到了 i i ,如果 i i s t a c k [ t o p ] stack[top] 的交点在 s t a c k [ t o p ] stack[top] s t a c k [ t o p 1 ] stack[top-1] 的交点的左边,那么 s t a c k [ t o p ] stack[top] 就可以被删掉了。

查询的话直接二分在哪个函数上即可。

现在有了插入删除操作,我们直接线段树分治。对于每个线段树节点都像上面那样处理;对于一个询问,查询它在线段树上所有祖先的答案取最小值即可。

复杂度 O ( n log 2 n ) O(n \log^2 n)

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }

const int MAXN = 300005;
struct Func {
    int a; LL b;
    LL calc(int x) const {
        LL t = x - a;
        return t * t * t * t + b;
    }
} fun[MAXN];

int inter(const Func &a, const Func &b) {
    int l = 0, r = 5e4 + 5;
    while (l + 1 < r) {
        int mid = (l + r) >> 1;
        if (a.calc(mid) < b.calc(mid)) l = mid;
        else r = mid;
    }
    return l;
}

int tn, T, n, m, beg[MAXN], ed[MAXN], id[MAXN];
struct Seg { Func f; int x; };
vector<Seg> tr[MAXN];
struct Qry { int t, p; };
vector<Qry> qry;
void update(int a, int b, int d, int l = 1, int r = tn, int k = 1) {
    if (a > r || b < l) return;
    if (a <= l && b >= r) {
        while (!tr[k].empty() && (tr[k].back().f.a == fun[d].a ||
            tr[k].back().x >= inter(tr[k].back().f, fun[d])))
            tr[k].pop_back();
        if (tr[k].empty()) tr[k].push_back(Seg { fun[d], 0 });
        else tr[k].push_back(Seg { fun[d], inter(tr[k].back().f, fun[d]) });
        return;
    }
    int mid = (l + r) >> 1;
    update(a, b, d, l, mid, k << 1);
    update(a, b, d, mid + 1, r, k << 1 | 1);
}

LL ask(int k, int x) {
    LL ans = 9e18;
    for (k += tn - 1; k > 0; k >>= 1) {
        int l = 0, r = tr[k].size();
        if (!r) continue;
        while (l + 1 < r) {
            int mid = (l + r) >> 1;
            if (tr[k][mid].x < x) l = mid;
            else r = mid;
        }
        chkmin(ans, tr[k][l].f.calc(x));
    }
    return ans;
}

int main() {
    for (scanf("%d", &T); T--;) {
        scanf("%d%d", &n, &m);
        int tim = 1;
        for (int i = 1; i <= n; i++)
            scanf("%d%lld", &fun[i].a, &fun[i].b);
        for (int i = 1; i <= m; i++) {
            int o, a; LL b; scanf("%d%d", &o, &a);
            if (o == 1) {
                scanf("%lld", &b);
                fun[++n] = Func { a, b };
                beg[n] = ++tim;
            } else if (o == 2) {
                ed[a] = tim++;
            } else qry.push_back(Qry { tim, a });
        }
        for (int i = 1; i <= n; i++) id[i] = i;
        sort(id + 1, id + 1 + n, [&](int x, int y)
            { return fun[x].a == fun[y].a ? fun[x].b > fun[y].b : fun[x].a < fun[y].a; });
        for (tn = 1; tn < tim; tn <<= 1);
        for (int i = 1; i <= n; i++) {
            int d = id[i];
            if (ed[d] == 0) ed[d] = tim;
            update(beg[d], ed[d], d);
        }
        for (const Qry &q : qry) {
            LL t = ask(q.t, q.p);
            if (t > 8e18) puts("-1");
            else printf("%lld\n", t);
        }
        qry.clear();
        for (int i = 1; i <= tn * 2; i++)
            tr[i].clear();
        for (int i = 1; i <= n; i++)
            beg[i] = ed[i] = 0;
    }
    return 0;
}

1009

暴力可过题。考虑每种轮廓的外接矩形的面积和不超过 4 × 1 0 8 4 \times 10^8 ,直接暴力枚举外接矩形内的所有点,判一下在不在轮廓内即可。判断的话可以看他左边的轮廓线条数是不是奇数,是奇数就在里面,否则就在外面。

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> void chkmax(T &a, const T &b) { a = a < b ? b : a; }
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }

namespace IO {
    const int MAXR = 10000000;
    char _READ_[MAXR], _PRINT_[MAXR];
    int _READ_POS_, _PRINT_POS_, _READ_LEN_;
    inline char readc() {
    #ifndef ONLINE_JUDGE
        return getchar();
    #endif
        if (!_READ_POS_) {
            if (feof(stdin)) return -1;
            _READ_LEN_ = fread(_READ_, 1, MAXR, stdin);
        }
        char c = _READ_[_READ_POS_++];
        if (_READ_POS_ == _READ_LEN_) _READ_POS_ = 0;
        return c;
    }
    template<typename T> inline int read(T &x) {
        x = 0; register int flag = 1, c;
        while (((c = readc()) < '0' || c > '9') && c != '-')
            if (c < 0) return -1;
        if (c == '-') flag = -1; else x = c - '0';
        while ((c = readc()) >= '0' && c <= '9') x = x * 10 - '0' + c;
        x *= flag; return 0;
    }
    inline int read(char *s) {
        register int len = 0, c;
        while (isspace(c = readc()) || c <= 0)
            if (c < 0) return -1;
        s[len++] = c;
        while (!isspace(c = readc()) && c) s[len++] = c;
        s[len] = 0;
        return len;
    }
    template<typename T1, typename ...T2> inline int read(T1 &a, T2&... x) {
        return read(a) | read(x...);
    }
    inline void ioflush() { fwrite(_PRINT_, 1, _PRINT_POS_, stdout), _PRINT_POS_ = 0; fflush(stdout); }
    inline void printc(char c) {
        if (!c) return;
        _PRINT_[_PRINT_POS_++] = c;
        if (_PRINT_POS_ == MAXR) ioflush();
    }
    template<typename T> inline void print(T x, char c = '\n') {
        if (x < 0) printc('-'), x = -x;
        if (x) {
            static char sta[20];
            register int tp = 0;
            for (; x; x /= 10) sta[tp++] = x % 10 + '0';
            while (tp > 0) printc(sta[--tp]);
        } else printc('0');
        printc(c);
    }
    inline void print(char *s, char c = '\n') {
        for (int i = 0; s[i]; i++) printc(s[i]);
        printc(c);
    }
    inline void print(const char *s, char c = '\n') {
        for (int i = 0; s[i]; i++) printc(s[i]);
        printc(c);
    }
    template<typename T1, typename ...T2> inline void print(T1 x, T2... y) {
        print(x, ' '), print(y...);
    }
}

const int MAXN = 405, MAXM = 400005;
int mt[MAXN][MAXN], vis[MAXM], n, m, Q, T, tim;
int tag[MAXN][MAXN];
char str[MAXM];

int main() {
    for (IO::read(T); T--;) {
        IO::read(n, m, Q);
        for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            IO::read(mt[i][j]);
        while (Q--) {
            int x, y; IO::read(x, y);
            int l = IO::read(str), x1 = n, y1 = m, x2 = 0, y2 = m;
            for (int i = 0; i < l; i++) {
                chkmin(x1, x), chkmin(y1, y);
                chkmax(x2, x), chkmax(y2, y);
                switch (str[i]) {
                    case 'L': tag[x--][y + 1] ^= 1; break;
                    case 'R': tag[++x][y + 1] ^= 1; break;
                    case 'D': --y; break;
                    case 'U': ++y; break;
                }
            }
            ++x1, ++y1;
            ++tim;
            int ans = 0;
            for (int i = x1; i <= x2; i++) {
                int t = 0;
                for (int j = y1; j <= y2; j++) {
                    t ^= tag[i][j];
                    if (t && vis[mt[i][j]] != tim)
                        vis[mt[i][j]] = tim, ++ans;
                    tag[i][j] = 0;
                }
                tag[i][y2 + 1] = 0;
            }
            IO::print(ans);
        }
    }
    IO::ioflush();
    return 0;
}

1010

暴搜题,每种物品至少会选择一个,因此复杂度最坏为 O ( 3 n / 3 ) O(3^{n/3})

#include<bits/stdc++.h>
using namespace std;
int test,n,k,a[55],b[55],c[55],d[55];
int cnt[55],id[55][55],sa,sb,sc,sd,t,mx;
long long ans;
void dfs(int i)
{
    if (i==mx+1)
    {
        ans=max(ans,1ll*(100+sa)*(100+sb)*(100+sc)*(100+sd));
        return;
    }
    if (!cnt[i])
    {
        dfs(i+1);
        return;
    }
    for (int j=1;j<=cnt[i];j++)
    {
        sa+=a[id[i][j]];sb+=b[id[i][j]];sc+=c[id[i][j]];sd+=d[id[i][j]];
        dfs(i+1);
        sa-=a[id[i][j]];sb-=b[id[i][j]];sc-=c[id[i][j]];sd-=d[id[i][j]];
    }
}
int main()
{
    scanf("%d",&test);
    while(test--)
    {
        scanf("%d%d",&n,&k);memset(cnt,0,sizeof(cnt));mx=0;
        for (int i=1;i<=n;i++)
        {
            scanf("%d%d%d%d%d",&t,&a[i],&b[i],&c[i],&d[i]);
            mx=max(mx,t);
            cnt[t]++;id[t][cnt[t]]=i;
        }
        ans=-1e18;sa=sb=sc=sd=0;
        dfs(1);
        printf("%lld\n",ans);
    }
    return 0;
}

1011

这个题和第一场冲了……导致一个 hard 变成了 medium-easy。考虑令一个物品的两种权值为 a i + b i , a i a_i+b_i,a_i ,就转化成了这一题的第一部分。代码就不放第二次了。

1012

刚开始看成了字符串的编辑距离,写了半天发现没过样例……才发现这题水的不行。

f ( i , j , k ) f(i,j,k) 表示最大的 x x 使得 A [ x . . . i ] A[x...i] B [ 1... j ] B[1...j] 的 LCS 长度至少为 k k 。直接 O ( n 2 ) O(n^2) 转移即可,询问的话找到最大的 k k 使得 f ( r , m , k ) l f(r,m,k) \ge l 即可。复杂度 O ( n m 2 ) O(nm^2)

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> void chkmax(T &a, const T &b) { a = a < b ? b : a; }
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }

const int MAXN = 100005;
int f[MAXN][25][25], n, m, Q, T;
char A[MAXN], B[25];

int main() {
    for (scanf("%d", &T); T--;) {
        scanf("%s%s", A + 1, B + 1);
        n = strlen(A + 1), m = strlen(B + 1);
        f[0][0][0] = 1;
        for (int i = 1; i <= n; i++) {
            f[i][0][0] = i + 1;
            for (int j = 1; j <= m; j++) {
                f[i][j][0] = i + 1;
                if (A[i] == B[j]) for (int k = 1; k <= m; k++)
                    f[i][j][k] = f[i - 1][j - 1][k - 1];
                else for (int k = 1; k <= m; k++)
                    f[i][j][k] = max(f[i - 1][j][k], f[i][j - 1][k]);
            }
        }
        scanf("%d", &Q);
        while (Q--) {
            int l, r; scanf("%d%d", &l, &r);
            int ans = 0;
            for (int k = 1; k <= m; k++)
                if (f[r][m][k] >= l) chkmax(ans, k);
                else break;
            printf("%d\n", r - l + 1 + m - 2 * ans);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/WAautomaton/article/details/107584307