P1129 [ZJOI2007] 矩阵游戏
链接
网格图是一个经典的二分图模型,在这题中.交换行列实际上就是交换节点的相对编号,但是对原本的边的连接情况仍然不变.
我们规定左部是行,右部是列.
比如说(1,3)上是1.对应着左部编号1连接右部编号3.
如果我们实行行变换,就是把这条对应的左部节点编号1情况换到另外一个节点的编号情况.比如交换行1与行2.
此时变为了(2,3)有1,对应编号2的点连接到右部编号3.
同理,如果交换列了,就是交换右部点的连接情况.
题目有解的情况是有n条这样的边.
(1,1),(2,2),(3,3).
既然我们从行列交换中发现,我们可以交换两个节点的相对编号来实现.行交换就是更改左部点的相对编号,列交换是更改右部的相对编号.
再仔细思考下,如果我们对于这个图含有一个完美匹配的时候,这个网格该是什么样的形状的.
就是存在一种方案,比如说n=3的时候
0 0 1
1 0 0
0 1 0
像这种,人为很容易地就能知道如何更改的情况.
把上面的图用二分图划出来,就会发现,只要通过行列交换,变换起始点与终点的编号,总是能构造出(1,1),(2,2)(3,3)这样的边.
所以,这个问题转化为求是否存在一个完美匹配.
跑网络流即可.
#include<bits/stdc++.h>
using namespace std;
const int maxn = 400+5;
const int INF = 1e9+7;
typedef long long ll;
typedef pair<int,int> pii;
struct Edge{
int from,to,cap,flow;//起点,终点,容量,流量
};
struct Dicnic{
int n,m,s,t;//点,边,源点,汇点
vector<Edge> edges;//边集
vector<int> G[maxn];
bool vis[maxn];//bfs 使用的
int d[maxn];//起点到i的距离,就是bfs得到的层次图,下次dfs沿着d走
int cur[maxn];//当前弧优化使用
void addEdge(int from,int to,int cap){
edges.push_back({from,to,cap,0});
edges.push_back({to,from,0,0});//反向边
m = edges.size();
G[from].push_back(m-2);G[to].push_back(m-1);
//正边的编号都是偶数,反向边的编号都是奇数
}
bool bfs(){
memset(vis,0,sizeof(vis));
queue<int> Q;Q.push(s);
d[s]=0;vis[s]=true;
while(!Q.empty()){
int x = Q.front();Q.pop();
for(auto v : G[x]){
Edge &e = edges[v];//获得边的编号
if(!vis[e.to]&&e.cap>e.flow){
//只有未被访问过,而且位于残量网络中的弧才考虑
vis[e.to]=1;
d[e.to]=d[x]+1;Q.push(e.to);
}
}
}
return vis[t];
}
int dfs(int x,int a){
//寻找增广路,a代表目前经过的路中,每个边剩余的流量
if(x==t||a==0) return a;//到达终点/残量为0
int flow = 0,f;
for(int &i = cur[x];i<G[x].size();i++){
Edge &e = edges[G[x][i]];
if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0){
//首先要按层次图走,然后当前增加流量要是增广路中流量最小的的
e.flow+=f;
edges[G[x][i]^1].flow-=f;//正边加流量,反边减流量
flow+=f;a-=f;
if(a==0) break;
}
}
return flow;
}
int Maxflow(int s,int t){
this->s=s;this->t=t;
int flow=0;
while(bfs()){
memset(cur,0,sizeof(cur));flow+=dfs(s,INF);
}
return flow;
}
};
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;cin>>T;
while(T--){
Dicnic g;
int n;cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int ok;cin>>ok;
if(ok){
g.addEdge(i,n+j,1);
}
}
}
//源点
int s = 0,t=2*n+2;
for(int i=1;i<=n;i++) g.addEdge(s,i,1);
for(int i=1;i<=n;i++) g.addEdge(n+i,t,1);
int flow = g.Maxflow(s,t);
if(flow==n) cout<<"Yes\n";
else cout<<"No\n";
}
}
B. Fake Plastic Trees
链接
给一个树带点权.初始点权为0,希望每个树的点权介于区间
[
l
v
,
r
v
]
[l_v,r_v]
[lv,rv]
定义一个操作:对于点
v
v
v到根1路径上所有节点,增加一个值
c
v
c_v
cv,但是要求从根到
v
v
v的值
c
v
c_v
cv是一个非递减的数字.
求最少操作数使得每个节点的点权都处于它们对应的区间上.
思考:对于一个叶子结点,因为其区间至少是大于1的,意味着每个叶子都至少占用一次操作数,
既然希望操作数最少,而且叶子加完后,下一次选择的路径也不会包含它们,所以一种可能最优的情况是叶子直接取
r
v
r_v
rv,对于它的父亲
u
u
u,可以选择的区间
[
0
,
m
i
n
(
r
u
,
r
s
o
n
)
]
[0,min(r_u,r_{son})]
[0,min(ru,rson)],这暗示父亲在满足儿子节点的定义下,可以额外增加的值是可以计算出来的.
令
v
a
l
u
val_u
valu =
m
i
n
(
r
u
,
∑
r
s
o
n
)
min(r_u,\sum{r_{son}})
min(ru,∑rson)
继续思考,既然儿子的值已经被计算出来了,父亲这个点也已经不再需要额外考虑了,索性能加就加,加的值取
v
a
l
u
val_u
valu就行.
如果
v
a
l
u
>
=
l
u
val_u>=l_u
valu>=lu,说明
u
u
u这个点已经不用继续考虑了,此时该点的取值就是
v
a
l
u
val_u
valu
,否则,这个点就成为一个新的"叶子",下一次新增加的值希望最大,取
r
u
r_u
ru
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e6+5;
const int INF = 1e9+7;
typedef long long ll;
typedef pair<int,int> pii;
#define all(a) (a).begin(), (a).end()
vector<int> G[maxn];
int l[maxn],r[maxn];
int dp[maxn];//the number of op of the subtree(u)
int add[maxn];//the number of u.
void init(int n){
for(int i=0;i<=n;i++) G[i].clear();
for(int i=0;i<=n;i++) dp[i]=add[i]=0;
}
void dfs(int u,int fa){
int child = 0;
int val = 0;
for(auto v : G[u]){
if(v==fa) continue;
dfs(v,u);child++;
val += add[v];
dp[u]+=dp[v];
}
if(child==0) dp[u]=1,add[u]=r[u];
else{
val = min(r[u],val);
if(val<l[u]) dp[u]+=1,add[u] = r[u];
else add[u]=val;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;cin>>T;
while(T--){
int n;cin>>n;
init(n);
for(int i=2;i<=n;i++){
int p;cin>>p;
G[i].push_back(p);
G[p].push_back(i);
}
for(int i=1;i<=n;i++) cin>>l[i]>>r[i];
dfs(1,0);
cout<<dp[1]<<"\n";
}
}
P1122 最大子树和
链接
求一个树的最大子树和.好像就是这样.dp乱搞一下先.
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
const int INF = 1e9+7;
typedef long long ll;
typedef pair<int,int> pii;
#define all(a) (a).begin(), (a).end()
vector<ll> G[maxn];
ll val[maxn];ll dp[maxn];
void dfs(int u,int fa){
dp[u] = val[u];
for(auto v : G[u]){
if(v==fa) continue;
dfs(v,u);
if(dp[v]>0) dp[u] += dp[v];
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
ll n;cin>>n;
ll ans = 1LL*INF*100000*-1;
for(int i=1;i<=n;i++) cin>>val[i],ans=max(val[i],ans);
for(int i=1;i<=n-1;i++){
int u,v;cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,0);
ans = max(ans,dp[1]);
// for(auto v : G[1]){
// ans = max(ans,dp[v]);
// }
cout<<ans<<"\n";
return 0;}
摸了,今天状态不佳