CF构造两道
都是用的调整法(而且我都做不出来),还是记录一下吧,毕竟我是构造废物(虽然每个方面都很废物)。
CF901D Weighting a Tree
Solution
我们首先考虑树的情况,可以发现我们可以一路递归上去,最后只需要判断根节点是否合法。
那么考虑非树的情况,发现在dfs树上有返祖边,如果把它们权值都设为 \(0\) 那么就是树的情况。但是发现如果根节点不合法,那么我们就需要调整它们,可以发现的是你改变偶环返祖边的权值对根节点不会有任何影响,因为你修改的权值一正一负最后会抵消掉,同理,奇环返祖边就会产生 \(+/-2\) 的贡献,那么我们就可以随便选一条改一下就好了。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define int long long
#define MAXN 200005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
int n,m;
struct edge{
int v,nxt;
}e[MAXN << 1];
int toop = 1,head[MAXN];
int D[MAXN],C[MAXN];
void add_edge (int u,int v){
e[++ toop] = edge{v,head[u]},head[u] = toop;
e[++ toop] = edge{u,head[v]},head[v] = toop;
}
bool vis[MAXN],cho[MAXN];int dep[MAXN],val[MAXN];
void dfs1 (int u,int fa){
dep[u] = dep[fa] + 1,vis[u] = 1;
for (Int i = head[u];i;i = e[i].nxt){
int v = e[i].v;
if (!vis[v])
dfs1 (v,u),val[i >> 1] = C[v] - D[v],D[v] += val[i >> 1],D[u] += val[i >> 1],cho[i >> 1] = 1;
}
}
void putout (int typ){
if (typ){
puts ("YES");
for (Int i = 1;i <= m;++ i) write (val[i]),putchar ('\n');
}
else puts ("NO");
exit (0);
}
signed main(){
read (n,m);
for (Int x = 1;x <= n;++ x) read (C[x]);
for (Int i = 1,u,v;i <= m;++ i) read (u,v),add_edge (u,v);
dfs1 (1,0);
if (C[1] == D[1]) putout (1);
else if (C[1] - D[1] & 1) putout (0);
for (Int u = 1;u <= n;++ u) for (Int i = head[u];i;i = e[i].nxt) if (!cho[i >> 1]){
int v = e[i].v;
if (!(dep[u] - dep[v] & 1)){
int ned = C[1] - D[1];
-- C[u],-- C[v],memset (vis,0,sizeof (vis)),memset (D,0,sizeof (D)),dfs1 (1,0);
int chg = ned / (C[1] - D[1] - ned);
++ C[u],++ C[v],val[i >> 1] -= chg,C[u] += chg,C[v] += chg,memset (vis,0,sizeof (vis)),memset (D,0,sizeof (D)),dfs1 (1,0);
putout (1);
}
}
putout (0);
return 0;
}
CF1019C Sergey's problem
Solution
我们考虑这样选择一个集合,每次找到一个未选的点,选它,并删除它能到的节点。可以发现这样一定可以满足最后这些集合可以到达所有点。
问题在于你这样选出来的点可以满足后面选出来的点能连向前面的点,你发现在这种关系视作一种边,那么这是一个DAG,那么我们可以直接对于 \(v\) 在有选了的 \(u\) 满足存在 \(u\to v\) 的时候直接不选 \(v\),可以发现这样同样可以满足条件 2。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define MAXN 1000005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
int n,m,deg[MAXN];
vector <int> g[MAXN],T[MAXN];
bool f[MAXN],cho[MAXN],vis[MAXN];
signed main(){
read (n,m);
for (Int i = 1,u,v;i <= m;++ i)
read (u,v),g[u].push_back (v);
for (Int x = 1;x <= n;++ x) if (!vis[x]){
vis[x] = cho[x] = 1;
for (Int v : g[x]) vis[v] = 1;
}
for (Int u = 1;u <= n;++ u) if (cho[u])
for (Int v : g[u]) if (cho[v])
T[u].push_back (v),++ deg[v];
queue <int> q;
for (Int u = 1;u <= n;++ u) if (!deg[u] && cho[u]) q.push (u);
vector <int> Ans;
while (!q.empty()){
int u = q.front();q.pop ();
if (!f[u]) Ans.push_back (u);
for (Int v : T[u]) if(cho[v]){
f[v] |= (f[u] ^ 1);
// cout << u << " ---> " << v << endl;
if (!-- deg[v]) q.push (v);
}
}
write (Ans.size()),putchar ('\n');
for (Int x : Ans) write (x),putchar (' ');putchar ('\n');
return 0;
}
Summary
md,做构造题看来不是递归、二进制分组就是调整法,rnm。