第一問考慮動態加邊網絡流。按照志願順序加邊,如果能夠bfs成功就能成功增廣。記錄答案即可。
難點在第二問,如何確定最少增長几名呢。二分是比較好想到的。。有一個妙妙的做法就是,在第一問時存下每一個狀態的圖,然而第二問二分到相應的圖,上去增廣。小技巧就是不需要的邊及時刪掉。
這題還是比較妙的,動態加邊加上存殘圖。
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
const int MAXN=500;
const int INF=1e9+7;
int T,C,n,m,s,t,p[MAXN];
struct edge{
int to,w,rev;
edge(int a,int b,int c){
to=a,w=b,rev=c;
}
};
struct Gra{
vector<edge>vec[MAXN];
inline void add(int u,int v,int w){
vec[u].push_back(edge(v,w,vec[v].size()));
vec[v].push_back(edge(u,0,vec[u].size()-1));
}
inline void del(int u,int v){
vec[u].pop_back();
vec[v].pop_back();
}
queue<int>q;
int dep[MAXN];
bool bfs(){
memset(dep,0,sizeof(dep));
dep[s]=1;q.push(s);
while(q.size()){
int u=q.front();q.pop();
for(int i=0;i<vec[u].size();i++){
edge &E=vec[u][i];
int v=E.to;
if(E.w&&!dep[v]){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
if(!dep[t])return 0;
return 1;
}
int dfs(int u,int flow){
if(u==t||flow==0)return flow;
for(int i=0;i<vec[u].size();i++){
edge &E=vec[u][i];
int v=E.to;
if(dep[v]==dep[u]+1&&E.w){
int tmp=dfs(v,min(flow,E.w));
if(tmp){
E.w-=tmp;
vec[v][E.rev].w+=tmp;
return tmp;
}
}
}
return 0;
}
}G[MAXN];
vector<int>Pip[MAXN][MAXN];
int ans[MAXN];
void solve1(){
for(int i=1;i<=n;i++){
G[i]=G[i-1];
G[i].add(s,i,1);
for(int j=1;j<=m;j++){
for(int k=0;k<Pip[i][j].size();k++)G[i].add(i,n+Pip[i][j][k],1);
if(G[i].bfs()){
G[i].dfs(s,INF);
ans[i]=j;
break;
}
for(int k=Pip[i][j].size()-1;k>=0;k--)G[i].del(i,n+Pip[i][j][k]);
}
}
for(int i=1;i<=n;i++){
if(!ans[i])ans[i]=m+1;
printf("%d ",ans[i]);
}
puts("");
}
bool check(int x,int pos){
Gra TG=G[pos-1];
TG.add(s,x,1);
for(int i=1;i<=m;i++){
if(i>p[x])return 0;
for(int j=0;j<Pip[x][i].size();j++)TG.add(x,n+Pip[x][i][j],1);
if(TG.bfs()){
if(i<=p[x])return 1;
return 0;
}
}
return 0;
}
int find(int x){
int l=1,r=x-1,ans=x;
while(l<=r){
int mid=l+r>>1;
if(check(x,x-mid))r=mid-1,ans=mid;
else l=mid+1;
}
return ans;
}
void solve2(){
for(int i=1;i<=n;i++){
if(p[i]>=ans[i])printf("0 ");
else printf("%d ",find(i));
}
puts("");
}
void work(){
for(int i=1;i<=m;i++){
int tmp;
scanf("%d",&tmp);
G[0].add(n+i,t,tmp);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int tmp;
scanf("%d",&tmp);
if(!tmp)continue;
Pip[i][tmp].push_back(j);
}
}
for(int i=1;i<=n;i++)scanf("%d",&p[i]);
solve1();
solve2();
}
void mem(){
for(int i=0;i<=405;i++){
for(int j=0;j<=405;j++)G[i].vec[j].clear();
}
memset(p,0,sizeof(p));
memset(ans,0,sizeof(ans));
for(int i=0;i<=405;i++)
for(int j=0;j<=405;j++)Pip[i][j].clear();
}
int main(){
//freopen("mentor.in","r",stdin);
//freopen("mentor.out","w",stdout);
scanf("%d%d",&T,&C);
while(T--){
mem();
scanf("%d%d",&n,&m);
s=0,t=n+m+1;
work();
}
return 0;
}
2.林克卡特樹
對於題目名字。。2333。很有趣。
10%的數據,直接求帶負權的樹的直徑。記錄每個節點往下的最長鏈和次長鏈然後瞎算一下。
20%的數據,只刪一條邊連一條邊。我們考慮刪完一條邊以後答案是什麼樣的,可以從一個子樹中選出一條鏈,從另一個子樹中也選一個鏈,比較大小。也可以把他們首尾相接。所以直接枚舉刪邊,分別求直徑相加即可。
對於60%的數據,通過20%的數據我們想到了刪一條邊時,加的邊是選2個鏈接起來。更多的刪邊加邊不也是這樣嘛。
但有一種情況需要考慮,就是一條鏈上多個分叉,我們這樣是無論如何都接不起來的(成環),所以題目轉化爲了選k+1條點不相交的鏈。 表示以i爲根的子樹,已經選了j個鏈,其中鏈上的點有 條邊與點i相交。
轉移的時候分 子樹順序轉移。其中 是當前的答案, 是已經轉移過的答案,這樣就不會有衝突了,詳細見代碼..
代碼60pts
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=3e5+10;
const ll INF=1e18+7;
struct edge{
int to,next;ll w;
}e[MAXN<<1];
int head[MAXN],cnt=0,n,k;
inline void add(int u,int v,ll w){
e[++cnt]=(edge){v,head[u],w},head[u]=cnt;
e[++cnt]=(edge){u,head[v],w},head[v]=cnt;
}
int size[MAXN];
ll f[MAXN][105][3],g[105][3];
//f[i][0][0]=0;f[i][1][1]=0;
//f[j][0]=f[k][0..2] k<=j
void dfs(int u,int fa){
f[u][0][0]=f[u][1][1]=0;
size[u]=1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa)continue;
dfs(v,u);
for(int j=0;j<=k;j++)
for(int t=0;t<=2;t++)g[j][t]=-INF;
int limit=min(k,size[u]);
for(int j=0;j<=limit;j++){
for(int t=0;t<=size[v];t++){//此時的f還未被v子樹更新。
if(t+j>k+1)break;//兩條鏈連成一個時會-1
for(int tt=0;tt<=2;tt++)
for(int tt2=0;tt2<=2;tt2++)g[t+j][tt]=max(g[t+j][tt],f[u][j][tt]+f[v][t][tt2]);
if(t||j)g[t+j-1][2]=max(g[t+j-1][2],f[u][j][1]+f[v][t][1]+e[i].w);
// if(t||j)g[t+j][2]=max(g[t+j][2],f[u][j][1]+f[v][t][0]+e[i].w);
g[t+j][1]=max(g[t+j][1],f[u][j][0]+f[v][t][1]+e[i].w);//選k個鏈合併時改變了u節點的度數
// g[t+j][1]=max(g[t+j][1],f[u][j][0]+f[v][t][0]+e[i].w);
}
}
for(int j=0;j<=k;j++)
for(int t=0;t<=2;t++)f[u][j][t]=g[j][t];
size[u]+=size[v];
}
}
int main(){
// freopen("lct.in","r",stdin);
// freopen("lct.out","w",stdout);
scanf("%d%d",&n,&k);
k++;
for(int i=1;i<n;i++){
int u,v;ll w;
scanf("%d%d%lld",&u,&v,&w);
add(u,v,w);
}
for(int i=0;i<=n;i++){
for(int j=0;j<=k;j++)
f[i][j][0]=f[i][j][1]=f[i][j][2]=-INF;
}
dfs(1,1);
cout<<max(f[1][k][0],max(f[1][k][1],f[1][k][2]))<<endl;
}
T3不會寫 完