欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。
https://codeforces.com/contest/1697/problem/F
给定数字n,k生成一个数组A,数组要求相邻数字要求上升或相等,所有数字需要在1到k之间,同时要满足以下条件。
1 i x: 表示A[i]个数字不能等于x。
2 i j x: 表示A[i]+A[j] <= x。
3 i j x: 表示A[i]+A[j] >= x。
由于k范围很小,可枚举j从1-k, 用2个状态表示A[i]>=j 或者 A[i]<j,直接想到思路就是用2sat表示。
分析一下2sat建立边的过程。
根据基本上升条件
如果A[i]>=j 可以推出A[i]>=j-1, 如果A[i] < j则推出A[i]<j+1
然后是根据3个补充条件进行设置。
1 i x: 表示A[i]个数字不能等于x。那么可以设置 A[i]>=x+1 || A[i]<x
2 i j x: 表示A[i]+A[j] <= x。枚举 s从1到k, 如果A[i]>=s, 则A[j]<x-s+1, 具有对称性,将i,j对调即可。
3 i j x: 表示A[i]+A[j] >= x。枚举 s从1到k, 如果A[i]<s+1, 则A[j]>=x-s, 具有对称性,将i,j对调即可。
#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
#include <stack>
#include <vector>
#include <map>
#include <algorithm>
#include <assert.h>
using namespace std;
const int N = 6e5 +10;
const int E = 6e6+10;
// 边属性
class Edge {
public:
int toVertex;
int nextEdge;
};
// 点属性
class Node {
public:
int head;
int indu;
};
class Graph {
public:
Edge edges[E];
Node nodes[N];
int usedEdge=0;
Graph() {
usedEdge = 0;
}
void initEdge(int n) {
for(int i=0;i<=n;++i) {
nodes[i].head=-1;
nodes[i].indu = 0;
}
usedEdge = 0;
}
void addEdge(int a, int b) {
if(a==b) return;
edges[usedEdge].nextEdge = nodes[a].head;
nodes[a].head = usedEdge;
edges[usedEdge].toVertex = b;
nodes[b].indu++;
usedEdge++;
// cout<<"add edge: "<<a<<","<<b<<endl;
}
int dfn[N], low[N];
stack<int> st;
int deep, sum;
int color[N];
void initTarjan(int n) {
deep = 0;
sum=0;
memset(dfn, 0,sizeof(int)*n);
memset(low, 0,sizeof(int)*n);
memset(color, 0,sizeof(int)*n);
}
void tarjan(int u) {
dfn[u] = ++deep;
low[u] = deep;
st.push(u);
for(int i=nodes[u].head;i>=0;i = edges[i].nextEdge) {
int v = edges[i].toVertex;
if(!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if(!color[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if(dfn[u]==low[u]) {
color[u] = ++sum;
while(st.top()!=u) {
int v = st.top();
st.pop();
color[v]=sum;
}
st.pop();
}
}
map<int, int> loc;
bool topo(int n, bool deb) {
queue<int>qu;
for(int i=1;i<=n;++i) {
if(nodes[i].indu==0) {
qu.push(i);
}
}
int l=0;
while(!qu.empty()) {
int f = qu.front();
loc[f]=++l;
qu.pop();
for(int i=nodes[f].head;i>=0;i=edges[i].nextEdge) {
int v = edges[i].toVertex;
nodes[v].indu--;
if(nodes[v].indu==0) qu.push(v);
}
}
if(deb) cout<<l<<endl;
return l==n;
}
};
Graph og;
// i 不选择选择1-k, i+1 不选择选择1-k, 。。。。 i选择 1-k
int encode(int ind, int num, int k, bool sel) {
return 2*(ind*k + num) + sel;
}
pair<int, int> decode(int code, int k) {
code >>=1;
int ind = code/k;
int num = code%k;
return {ind, num};
}
// a>=av ? as => b>=bv ? bs
void addEdge(int a, int av, bool as, int b, int bv, bool bs, int k) {
int u = encode(a, av, k, as);
int v = encode(b, bv, k, bs);
og.addEdge(u,v);
}
void solve() {
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
og.initEdge(2*(k+2)*n+10);
// 对每个加限制条件
for(int i=0;i<n;++i) {
// 设置单值范围, A[i]>=av => A[i]>= j (j<=av)
for(int av = 0;av<=k;++av){
addEdge(i, av+1, true, i, av, true, k+2);
addEdge(i, av, false, i, av+1, false, k+2);
}
// 限制不能取0, 不能取k+1
addEdge(i, 1, false, i, 1, true, k+2);
addEdge(i, k+1, true, i, k+1, false, k+2);
// 添加递增条件
for(int av=0;av<=k+1;++av) {
if(i<n-1) {
addEdge(i, av, true, i+1, av, true, k+2);
addEdge(i+1, av, false, i, av, false, k+2);
}
}
}
for(int i=0; i<m; ++i) {
int t, a, b, x;
scanf("%d", &t);
switch (t)
{
case 1: // A[a] != x
scanf("%d%d", &a, &x);
--a;
// A[a]>=x+1 || A[x]<x
addEdge(a, x+1, false, a, x, false, k+2);
addEdge(a, x, true, a, x+1, true, k+2);
break;
case 2: // A[a]+A[b] <= x
scanf("%d%d%d", &a, &b, &x);
--a, --b;
// 枚举 a从1到x设置对应b不能选择的条件, 有对称性
for(int av=max(1, x-k); av<=k && av<x; ++av) {
addEdge(a, av, true, b, x-av+1, false, k+2);
addEdge(b, x-av+1, true, a, av, false, k+2);
addEdge(b, av, true, a, x-av+1, false, k+2);
addEdge(a, x-av+1, true, b, av, false, k+2);
}
break;
case 3:// A[a]+A[b]>=x
scanf("%d%d%d", &a, &b, &x);
--a, --b;
// 枚举 a从1到x设置对应b不能选择的条件, 有对称性
for(int av=max(1, x-k); av<=k && av < x; ++av) {
addEdge(a, av+1, false, b, x-av, true, k+2);
addEdge(b, x-av, false, a, av+1, true, k+2);
addEdge(b, av+1, false, a, x-av, true, k+2);
addEdge(a, x-av, false, b, av+1, true, k+2);
}
break;
default:
break;
}
}
og.initTarjan(2*(2+k)*n+10);
for(int i=0;i<2*(2+k)*n;++i) {
if(!og.dfn[i]) og.tarjan(i);
}
vector<int> ans(n);
for(int i=0;i<(k+2)*n;++i) {
if(og.color[2*i]==og.color[2*i+1]) {
auto p = decode(2*i+1, k+2);
// cout<<"il"<<p.first<<"|"<<p.second<<endl;
puts("-1");
return;
}
int code;
if(og.color[2*i]<og.color[2*i+1]) continue;
else code = 2*i+1;
// cout<<code<<endl;
auto p = decode(code, k+2);
ans[p.first] = p.second;
}
for(int i=0;i<n;++i) {
if(i)putchar(' ');
printf("%d", ans[i]);
}
// puts("---\n");
puts("");
}
int main() {
int t;
scanf("%d", &t);
while(t--) {
solve();
}
return 0;
}
/*
*/
本人码农,希望通过自己的分享,让大家更容易学懂计算机知识。