Wannafly挑戰賽9 E - 組一組

連接:https://www.nowcoder.net/acm/contest/71/E
來源:牛客網

c++

題目描述

有一個長爲 n 的數列 A,其中有 m 個限制條件,條件有兩種:
一、對於區間 [l,r],其區間元素按位或和等於 x
二、對於區間 [l,r],其區間元素按位與和等於 x
求出一個數列 A,使得知足給定的 m 個條件,保證有解。

輸入描述:

輸入第一行兩個正整數 n,m,意義如上
接下來 m 行,每行四個整數 op,l,r,x,表示一組限制
op = 1 表示是限制 1,op = 2 表示是限制 2

輸出描述:

輸出僅一行,n 個整數 a
i
 表示數列 A。要求 0 <= a
i
 < 10
9
示例1

輸入

4 3 
1 1 2 9 
2 3 4 2 
1 2 3 11

輸出

1 9 2 6

備註:

1<=n,m<=10^5, 1<=l<=r<=n, 0<=x<2^20

題解

差分約束系統,剪枝。spa

每一位分開考慮,能夠列出一系列不等式,只要求出一組可行解。.net

剪枝:code

對於某些位置,在沒有跑差分約束系統以前,就能夠肯定必定是$1$,也就是能夠多增長一些不等式,形如$sum[i] - sum[0] >= x$,用來剪枝。blog

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

const int maxn = 1e5 + 10;
int n, m;
int op[maxn], L[maxn], R[maxn], x[maxn];
int h[maxn], v[maxn * 10], w[maxn * 10], nx[maxn * 10];
int sz;
int ans[maxn];

int dis[maxn], f[maxn];
int sum[maxn];

void init() {
  for(int i = 0; i <= n; i ++) {
    h[i] = -1;
    sum[i] = 0;
  }
  sz = 0;
}

void add(int a, int b, int c) {
  //printf("%d -> %d : %d\n", a, b, -c);
  v[sz] = b;
  w[sz] = -c;
  nx[sz] = h[a];
  h[a] = sz ++;
}

void spfa() {
  queue<int> q;
  for(int i = 0; i <= n; i ++) {
    dis[i] = maxn;
    f[i] = 0;
  }
  dis[0] = 0;
  q.push(0);
  f[0] = 1;
  while(!q.empty()) {
    int top = q.front();
    q.pop();
    f[top] = 0;
    for(int i = h[top]; i != -1; i = nx[i]) {
      if(dis[top] + w[i] < dis[v[i]]) {
        dis[v[i]] = dis[top] + w[i];
        if(f[v[i]] == 0) {
          f[v[i]] = 1;
          q.push(v[i]);
        }
      }
    }
  }
}

int main() {
  scanf("%d%d", &n, &m);
  for(int i = 1; i <= m; i ++) {
    scanf("%d%d%d%d", &op[i], &L[i], &R[i], &x[i]);
  }
  for(int t = 0; t < 20; t ++) {
    init();
    // 0 <= sum[x] - sum[x - 1] <= 1
    // ! sum[x] - sum[x - 1] >= 0
    // ! sum[x - 1] - sum[x] >= -1
    for(int i = 1; i <= n; i ++) {
      add(i - 1, i, 0);
      add(i, i - 1, -1);
    }
    for(int i = 1; i <= m; i ++) {
      if(op[i] == 1) {
        if(x[i] & (1 << t)) {
          // [L, R] 至少有一個1
          // sum[R] - sum[L - 1] >= 1
          add(L[i] - 1, R[i], 1);
        } else {
          // [L, R] 全爲0
          // 0 <= sum[R] - sum[L - 1] <= 0
          // ! sum[R] - sum[L - 1] >= 0
          // ! sum[L - 1] - sum[R] >= 0
          add(L[i] - 1, R[i], 0);
          add(R[i], L[i] - 1, 0);
        }
      } else {
        if(x[i] & (1 << t)) {
          // [L, R] 全爲1
          // R - L + 1 <= sum[R] - sum[L - 1] <= R - L + 1
          // ! sum[R] - sum[L - 1] >= R - L + 1
          // ! sum[L - 1] - sum[R] >= -(R - L + 1)
          add(L[i] - 1, R[i], R[i] - L[i] + 1);
          add(R[i], L[i] - 1, -(R[i] - L[i] + 1));
          sum[L[i]] ++;
          sum[R[i] + 1] --;
        } else {
          // [L, R] 不全爲1
          // 0 <= sum[R] - sum[L - 1] <= R - L
          // ! sum[R] - sum[L - 1] >= 0
          // ! sum[L - 1] - sum[R] >= L - R
          add(L[i] - 1, R[i], 0);
          add(R[i], L[i] - 1, L[i] - R[i]);
        }
      }
    }
    for(int i = 1; i <= n; i ++) {
      sum[i] += sum[i - 1];
    }
    for(int i = 1; i <= n; i ++) {
      if(sum[i]) sum[i] = 1;
    }
    for(int i = 1; i <= n; i ++) {
      sum[i] += sum[i - 1];
      add(0, i, sum[i]);
    }
    spfa();
    for(int i = 1; i <= n; i ++) {
      dis[i] = -dis[i];
    }
    for(int i = n; i >= 1; i --) {
      dis[i] = dis[i] - dis[i - 1];
    }
    for(int i = 1; i <= n; i ++) {
      ans[i] = ans[i] + dis[i] * (1 << t);
    }
  }
  for(int i = 1; i <= n; i ++) {
    printf("%d", ans[i]);
    if(i < n) printf(" ");
    else printf("\n");
  }
  return 0;
}

/*
 v[j] - v[i] >= k, 問v[t] - v[s]最小值
 建邊 i -> j, 權值爲k, s到t的最長路就是答案
 */
相關文章
相關標籤/搜索