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];
int d[maxn];
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){
if(x==t||a==0) return a;
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];
int add[maxn];
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]);
cout<<ans<<"\n";
return 0;}
摸了,今天状态不佳
|