Ural 1099 Work Scheduling
一般图最大匹配模板题。
带花树:
A
C
C
o
d
e
\mathcal AC \ Code
AC Code
#include<bits/stdc++.h>
#define maxn 233
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
int n;
int info[maxn],Prev[maxn*maxn],to[maxn*maxn],cnt_e;
void Node(int u,int v){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v; }
int mat[maxn],nxt[maxn],F[maxn],typ[maxn],q[maxn],ql,qr;
int Find(int u){ return !F[u] ? u : F[u] = Find(F[u]); }
int Lca(int u,int v){
static int vis[maxn]={},tim=0;
for(++tim;;swap(u,v)) if(u=Find(u)){//切记不能放到for 中间!!!
if(vis[u] == tim) return u;
else vis[u] = tim , u = nxt[mat[u]];
}
}
void Blossom(int u,int v,int p){
for(;Find(u)!=p;){
nxt[u] = v , v = mat[u];
if(typ[v] == 1) typ[v] = 2 , q[++qr] = v;
F[u] = F[v] = p , u = nxt[v];
}
}
int BFS(int u){
rep(i,1,n) nxt[i] = F[i] = typ[i] = 0;
q[ql=qr=0]=u,typ[u] = 2;
for(;ql<=qr;){
u=q[ql++];
for(int j=info[u],v;j;j=Prev[j])
if(typ[v=to[j]] != 1 && Find(u) != Find(v)){
if(!typ[v]){
nxt[v] = u , typ[v] = 1;
if(mat[v]) typ[mat[v]] = 2 , q[++qr] = mat[v];
else{
for(int t;v;) t = mat[nxt[v]] , mat[nxt[v]] = v , mat[v] = nxt[v] , v = t;
return 1;
}
}
else{
int t= Lca(u,v);
Blossom(u,v,t) , Blossom(v,u,t);
}
}
}
return 0;
}
void MaxMatch(){
int ans = 0;
rep(i,1,n) if(!mat[i]) ans += BFS(i);
printf("%d\n",ans<<1);
rep(i,1,n) if(i < mat[i])
printf("%d %d\n",i,mat[i]);
}
int main(){
scanf("%d",&n);
for(int u,v;scanf("%d%d",&u,&v) != EOF;)
Node(u,v),Node(v,u);
MaxMatch();
}
HDU 3446 daizhenyang’s chess
给出一个有些障碍点不能落足的棋盘,给出一个兵象马的杂交国王的位置,两人依次移动,不能重复经过格子,不能操作者输,问先手是否必胜。
如果国王的位置不一定在最大匹配中,那么一定存在一个最大匹配,先手走完之后后手直接按照最大匹配走,那么先手走了之后后手总是有对应的走法,先手必败,否则后手不能总是有对应的走法,但是先手总是有对应后手的走法,先手必胜。
如何判断?
删点后再跑一次一般图最大匹配即可。
简单增广路随机法。
Count on a tree II SPOJ - COT2
多次询问树上的一条链上的颜色总数。
树上莫队模板题。
A C c o d e \mathcal AC \ code AC code
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define maxn 100005
#define lim 19
using namespace std;
int n,m,len;
int cnt[maxn],num[maxn],s[maxn],f[lim][maxn],lca[maxn],dep[maxn],ans[maxn];
int l[maxn],r[maxn],lb[maxn],c[maxn],a[maxn],b[maxn],bl[maxn],Siz=300,cnt_b;
int Prev[2*maxn],to[2*maxn],info[maxn],cnt_e,dfn[maxn],tim,seq[maxn];
bool ext[maxn];
int sum;
bool cmp(const int a,const int b){
if(lb[a]==lb[b]) return dfn[r[a]]<dfn[r[b]];
return lb[a]<lb[b];
}
void Node(int a,int b){
cnt_e++;
Prev[cnt_e]=info[a],info[a]=cnt_e,to[cnt_e]=b;
}
int sta[maxn],tp;
void dfs(int now,int ff){
dfn[now]=++tim;
f[0][now]=ff;
dep[now]=dep[ff]+1;
int bot=tp;
for(int i=info[now];i;i=Prev[i])
if(to[i]!=ff){
dfs(to[i],now);
if(tp-bot>=Siz)
{
++cnt_b;
while(tp>bot)
bl[sta[tp--]]=cnt_b;
}
}
sta[++tp]=now;
}
void init(){
for(int j=1;j<lim;j++)
for(int i=1;i<=n;i++)
f[j][i]=f[j-1][f[j-1][i]];
}
int Lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
for(int i=lim-1;i>=0;i--)
if((dep[u]-dep[v]) & (1<<i))
u=f[i][u];
if(u==v) return u;
for(int i=lim-1;i>=0;i--)
if(f[i][u]!=f[i][v])
u=f[i][u],v=f[i][v];
return f[0][u];
}
void modify(int now){
if(!ext[now]) sum+=(cnt[s[now]]++==0);
else sum-=(--cnt[s[now]]==0);
ext[now]^=1;
}
void Move(int a,int b,int c,int d){
if(dep[a]<dep[c]) swap(a,c);
while(dep[a]>dep[c]) modify(a),a=f[0][a];
while(a!=c) modify(a),a=f[0][a],modify(c),c=f[0][c];
if(dep[b]<dep[d]) swap(b,d);
while(dep[b]>dep[d]) modify(b),b=f[0][b];
while(b!=d) modify(b),b=f[0][b],modify(d),d=f[0][d];
}
int main(){
int u,v;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&s[i]),seq[i]=s[i];
sort(seq+1,seq+n+1);
int len=unique(seq+1,seq+1+n)-seq-1;
for(int i=1;i<=n;i++) s[i]=lower_bound(seq+1,seq+1+len,s[i])-seq;
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
Node(u,v);Node(v,u);
}
dfs(1,0);
init();
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
c[i]=i;l[i]=u;r[i]=v;lb[i]=bl[u];lca[i]=Lca(u,v);
}
sort(c+1,c+1+m,cmp);
l[c[0]]=r[c[0]]=1;
for(int i=1;i<=m;i++){
Move(l[c[i-1]],r[c[i-1]],l[c[i]],r[c[i]]);
sum+=(cnt[s[lca[c[i]]]]++==0);
ans[c[i]]=sum;
sum-=(--cnt[s[lca[c[i]]]]==0);
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}
CF835F Roads in the Kingdom
求基环树删去环上任意一边后直径最小值,直径定义为所有点对最近距离的最大值。
预处理出一系列:
点走树上的最长路,
在环上有一部分的最长路,
之前的最短路被枚举的边截了后的最短路的最长路。
即可
O
(
n
)
O(n)
O(n)求出答案。
如下图:
假设基环树上的点是
1...
n
1...n
1...n
断开
1
−
>
n
1->n
1−>n这条边,预处理
p
r
e
f
i
pref_i
prefi表示
i
i
i左边的绿线的最大值,
预处理
s
u
f
f
i
suff_i
suffi表示
i
i
i右边的绿线的最大值。
预处理
p
r
e
g
i
preg_i
pregi表示
i
i
i左边的红线的最大值。
预处理
s
u
f
g
i
sufg_i
sufgi表示
i
i
i右边的红线的最大值。
那么断开
i
−
>
i
+
1
i->i+1
i−>i+1的边后的答案就是:
max
(
p
r
e
f
i
+
s
u
f
f
i
+
w
1
,
n
,
p
r
e
g
i
,
s
u
f
g
i
)
\max(pref_i + suff_i + w_{1,n} ,preg_i , sufg_i )
max(prefi+suffi+w1,n,pregi,sufgi)
之类的。
A C C o d e \mathcal AC \ Code AC Code
#include<bits/stdc++.h>
#define maxn 200005
#define LL long long
#define inf 0x3f3f3f3f3f3f3f3fll
#define pb push_back
using namespace std;
int n;
int info[maxn],Prev[maxn<<1],to[maxn<<1],cst[maxn<<1],cnt_e;
void Node(int u,int v,int w){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=w; }
vector<int>L,C;
bool vis[maxn];
int sta[maxn],csta[maxn],in[maxn],cnt=0;
LL S[maxn],f[maxn],p1[maxn],p2[maxn],s1[maxn],s2[maxn],Ans1,Ans2;
bool flg = 0;
void dfs0(int u,int ff){
vis[u] = 1;
sta[++sta[0]] = u;
for(int i=info[u],v;i;i=Prev[i])
if((v=to[i])!=ff){
if(!vis[v]) csta[sta[0]+1]=cst[i],dfs0(v,u);
else{
for(int t=-1;t!=v;){
L.pb(t = sta[sta[0]]) , C.pb(csta[sta[0]--]);
if(t == v) C.back() = cst[i];
in[t] = 1;
}
flg = 1;
return ;
}
if(flg) return;
}
--sta[0];
}
void dfs1(int u,int ff){
for(int i=info[u],v;i;i=Prev[i])
if((v=to[i])!=ff && !in[v]){
dfs1(v,u);
Ans1 = max(Ans1 , f[v] + f[u] + cst[i]);
f[u] = max(f[u] , f[v] + cst[i]);
}
}
int main(){
scanf("%d",&n);
for(int i=1,u,v,w;i<=n;i++){
scanf("%d%d%d",&u,&v,&w);
Node(u,v,w),Node(v,u,w);
}
dfs0(1,0);
int m = L.size();
for(int i=0;i<m;i++)
dfs1(L[i],0),S[i]=(i?S[i-1]+C[i-1]:0);
S[m] = S[m-1] + C[m-1];
LL tmp = -inf;
for(int i=0;i<m;i++){
p1[i] = max(i?p1[i-1]:-inf,S[i]+f[L[i]]);
p2[i] = max(i?p2[i-1]:-inf,S[i]+f[L[i]]+tmp);
tmp=max(tmp,f[L[i]]-S[i]);
}
tmp = -inf;
for(int i=m-1;i>=0;i--){
s1[i] = max(i<m-1?s1[i+1]:-inf,f[L[i]]-S[i]);
s2[i] = max(i<m-1?s2[i+1]:-inf,f[L[i]]-S[i]+tmp);
tmp=max(tmp,f[L[i]]+S[i]);
}
Ans2 = p2[m-1];
for(int i=1;i<m;i++) Ans2 = min(Ans2,max(S[m]+p1[i-1]+s1[i],max(p2[i-1],s2[i])));
printf("%lld\n",max(Ans1,Ans2));
}
【WC2013】糖果公园
树上带修莫队模板题。
A
C
C
o
d
e
\mathcal AC \ Code
AC Code
#include<bits/stdc++.h>
#define N 100005
using namespace std;
struct edge{int to,next;}e[N*2];
struct Qtype{int l,r,t,id;}a[N],b[N];
int dfn[N],ed[N],id[N*2],be[N*2],v[N];
int fa[N][18],c[N],sum[N],last[N];
int w[N],head[N],dep[N],vis[N];
long long now,ans[N];
int n,m,tot,Tim;
#define gc() getchar()
int read(){
int x=0;
char ch=gc();
for (;ch<'0'||ch>'9';ch=gc());
for (;ch>='0'&&ch<='9';ch=gc())
x=x*10-48+ch;
return x;
}
void add(int x,int y){
e[++tot]=(edge){y,head[x]};
head[x]=tot;
}
void dfs(int x){
dfn[x]=++Tim;
id[Tim]=x;
for (int i=1;i<=17;i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
for (int i=head[x];i;i=e[i].next)
if (e[i].to!=fa[x][0]){
fa[e[i].to][0]=x;
dep[e[i].to]=dep[x]+1;
dfs(e[i].to);
}
ed[x]=++Tim;
id[Tim]=x;
}
int lca(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
int tmp=dep[x]-dep[y];
for (int i=0;i<=17;i++,tmp/=2)
if (tmp&1) x=fa[x][i];
for (int i=17;i>=0;i--)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return x==y?x:fa[x][0];
}
bool cmp(Qtype x,Qtype y){
if (be[x.l]!=be[y.l])
return be[x.l]<be[y.l];
if (be[x.r]!=be[y.r])
return be[x.r]<be[y.r];
return x.t<y.t;
}
void upd2(int x){
if (vis[x])
now-=1ll*v[c[x]]*w[sum[c[x]]--];
else now+=1ll*v[c[x]]*w[++sum[c[x]]];
vis[x]^=1;
}
void upd1(int x,int y){
if (vis[x]){
upd2(x);
c[x]=y;
upd2(x);
}
else c[x]=y;
}
int main(){
int Q;
n=read(); m=read(); Q=read();
for (int i=1;i<=m;i++) v[i]=read();
for (int i=1;i<=n;i++) w[i]=read();
for (int i=1;i<n;i++){
int x=read(),y=read();
add(x,y); add(y,x);
}
for (int i=1;i<=n;i++)
last[i]=c[i]=read();
dfs(1);
int sz=(int)pow(n,2.0/3);
for (int i=1;i<=Tim;i++)
be[i]=(i-1)/sz;
int cntQ=0,cntM=0;
while (Q--){
int t,l,r;
t=read(); l=read(); r=read();
if (t){
if (dfn[l]>dfn[r]) swap(l,r);
a[++cntQ].r=dfn[r];
a[cntQ].id=cntQ;
a[cntQ].t=cntM;
a[cntQ].l=(lca(l,r)==l?dfn[l]:ed[l]);
}
else{
b[++cntM].l=l;
b[cntM].t=last[l];
last[l]=b[cntM].r=r;
}
}
sort(a+1,a+cntQ+1,cmp);
int l=1,r=0,t=1;
for (int i=1;i<=cntQ;i++){
for (;t<=a[i].t;t++) upd1(b[t].l,b[t].r);
for (;t>a[i].t;t--) upd1(b[t].l,b[t].t);
for (;l>a[i].l;upd2(id[--l]));
for (;l<a[i].l;upd2(id[l++]));
for (;r<a[i].r;upd2(id[++r]));
for (;r>a[i].r;upd2(id[r--]));
int x=id[l],y=id[r],z=lca(x,y);
if (x!=z&&y!=z) upd2(z);
ans[a[i].id]=now;
if (x!=z&&y!=z) upd2(z);
}
for (int i=1;i<=cntQ;i++)
printf("%lld\n",ans[i]);
}
王室联邦
简单树形DP。
A
C
C
o
d
e
\mathcal AC \ Code
AC Code
#include <cstdio>
#define N 1005
#define M 2005
using namespace std;
int n,B,sz,cnt,tot,info[N],to[M],Prev[M],st[N],rt[N],bel[N];
void add(int u,int v) { to[++tot]=v,Prev[tot]=info[u],info[u]=tot; }
void dfs(int u,int p) {
int cnr=sz;
for(int i=info[u];i;i=Prev[i]) if((v=to[i])^p){
dfs(v,u);
if(sz-cnr>=B) {
rt[++cnt]=u;
while(sz>cnr) bel[st[sz--]]=cnt;
}
}
st[++sz]=u;
}
int main() {
scanf("%d%d",&n,&B);
for(int i=1;i<n;++i) {
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs(1,0);
if(!cnt) rt[++cnt]=1;
while(sz) bel[st[sz--]]=cnt;
printf("%d\n",cnt);
for(int i=1;i<=n;++i) printf("%d%c",bel[i]," \n"[i==n]);
for(int i=1;i<=cnt;++i) printf("%d%c",rt[i]," \n"[i==cnt]);
}
[ARC079D] Namori Grundy
给出一颗基环内向树,判断是否有一种分配权值的方案使得每个点的权值是指向他的点的 m e x \rm mex mex。
把树上从底向上递推(设叶子的权值为
0
0
0)。
然后在环上如果有奇数个点,并且他们权值一样,则无解,
否则有解,具体是在
a
u
=
a
v
a_u = a_v
au=av时让
a
u
=
a
v
+
1
a_u = a_v +1
au=av+1
具体证明:
环边的影响是,如果有
x
−
>
y
x->y
x−>y,并且
a
(
x
)
=
a
(
y
)
a(x)=a(y)
a(x)=a(y)那么此时
a
(
x
)
a(x)
a(x)会变大为
a
’
(
x
)
a’(x)
a’(x),继而若
z
−
>
x
z->x
z−>x并且
a
(
z
)
=
a
’
(
x
)
a(z)=a’(x)
a(z)=a’(x),那么
a
(
z
)
a(z)
a(z)会变大,依次类推。
显然有影响的边
x
−
>
y
x->y
x−>y当且仅当
a
(
x
)
≥
a
(
y
)
a(x)\geq a(y)
a(x)≥a(y),这样就能够把环拆成若干链,不同的链互补影响,并且每一条链都一定可以被改好。
(注意到如果外面的树在
a
(
x
)
a(x)
a(x)增大后会让
a
(
x
)
a(x)
a(x)继续增大,那么还是可以把环拆成若干条链,不影响。)
唯一不能拆成多条链的情况就是环上的值都一样,那么环长为偶数的时候时候我们显然可以简单构造出一组解比如
x
,
x
+
1
,
x
,
x
+
1
x,x+1,x,x+1
x,x+1,x,x+1这样的。
环长为奇数并且值都为
x
x
x的时候。
每个不是
x
x
x的点都需要一个
x
x
x来提供支持。
那么就一定会有一个
x
x
x落单,这个
x
x
x会指向另一个
x
x
x,矛盾。
所以我们就证明了环为奇数而且
a
(
x
)
a(x)
a(x)都相同时无解。
#include<bits/stdc++.h>
#define maxn 400005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
int n,fa[maxn];
int info[maxn],Prev[maxn<<1],to[maxn<<1],cnt_e=1;
void Node(int u,int v){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v; }
int ar[maxn],onc[maxn],a[maxn];
int vis[maxn],sta[maxn],tp;
void dfs0(int u,int ff){
vis[u] = 1;
sta[++tp] = u;
for(int i=info[u],v;i;i=Prev[i]) if(i^ff){
v=to[i];
if(!vis[v]) dfs0(v,(i^1));
else if(!onc[v]){
for(int t=-1;t!=v;){
t = sta[tp--];
ar[++ar[0]] = t;
onc[t] = 1;
}
per(i,ar[0],1) sta[++tp] = ar[i];
}
}
if(sta[tp] == u) tp--;
}
void dfs1(int u,int ff){
static int vis[maxn],tim;
for(int i=info[u],v;i;i=Prev[i]) if((v=to[i]) ^ ff && !onc[v])
dfs1(v,u);
++tim;
for(int i=info[u],v;i;i=Prev[i]) if((v=to[i]) ^ ff && !onc[v])
vis[a[v]] = tim;
a[u] = 0;
for(;vis[a[u]] == tim; a[u]++);
}
int main(){
scanf("%d",&n);
rep(i,1,n){
scanf("%d",&fa[i]);
Node(fa[i],i),Node(i,fa[i]);
}
dfs0(1,0);
rep(i,1,ar[0]) dfs1(ar[i],0);
set<int>st;
rep(i,1,ar[0]) st.insert(a[ar[i]]);
if((ar[0] & 1) && st.size() == 1)
puts("IMPOSSIBLE");
else
puts("POSSIBLE");
}
NOI2012迷失游乐园
基环树上随机游走,不能走重复的点,求期望走的长度。
儿子数量:
s
z
i
sz_i
szi
父亲数量:
s
z
f
i
szf_i
szfi(环上的点的父亲是环上的邻居。)
则第一步往儿子走的时候,就确定他不能往回走了。
所以确定第一步往下走的期望长度为
f
i
=
1
s
z
i
∑
v
i
s
a
s
o
n
o
f
i
f
v
+
w
i
,
v
f_i = \frac 1{sz_i}\sum_{v \ \ is \ a \ son \ of \ i} f_v + w_{i,v}
fi=szi1∑v is a son of ifv+wi,v
对于不在环上的点
i
i
i,第一步往上走的期望长度为:
g
i
=
w
i
,
f
a
i
+
g
f
a
i
s
z
f
f
a
i
+
f
f
a
i
(
s
z
f
a
i
−
1
)
−
w
i
,
f
a
i
s
z
i
+
s
z
f
i
−
1
g_i = w_{i,fa_i} + \frac {g_{fa_i}szf_{fa_i} + f_{fa_i}(sz_{fa_i}-1)-w_{i,fa_i}}{sz_i+szf_i-1}
gi=wi,fai+szi+szfi−1gfaiszffai+ffai(szfai−1)−wi,fai
对于环上的点
i
i
i,注意到环上节点很少,直接
O
(
20
)
O(20)
O(20)计算答案即可。
g
i
=
1
2
∑
j
≠
i
P
i
,
j
(
f
j
s
z
j
s
z
j
+
1
+
∑
e
d
g
e
k
b
e
t
w
e
e
n
t
h
e
r
o
a
d
o
f
i
a
n
d
j
w
k
)
g_i = \frac 12\sum_{j\neq i}P_{i,j} (\frac {f_{j}{sz_j}}{sz_j+1} + \sum_{edge \ k \ between \ the \ road \ of \ i \ and \ j} w_k)
gi=21∑j=iPi,j(szj+1fjszj+∑edge k between the road of i and jwk)
这是顺时针的式子,逆时针再算一次即可。
a
n
s
=
1
n
∑
i
f
i
s
z
i
+
g
i
s
z
f
i
s
z
i
+
s
z
f
i
ans = \frac 1n\sum_{i} \frac {f_isz_i + g_iszf_i}{sz_i+szf_i}
ans=n1∑iszi+szfifiszi+giszfi
代码风格:只要dfs写的多
A
C
C
o
d
e
\mathcal AC \ Code
AC Code
#include <bits/stdc++.h>
#define maxn 100005
#define rep(i, j, k) for (int i = (j), LIM = (k); i <= LIM; i++)
#define per(i, j, k) for (int i = (j), LIM = (k); i >= LIM; i--)
#define db double
using namespace std;
int n, m;
int info[maxn], Prev[maxn << 1], to[maxn << 1], cnt_e = 1;
db cst[maxn << 1], f[maxn], g[maxn], P[maxn];
void Node(int u, int v, int c) { Prev[++cnt_e] = info[u], info[u] = cnt_e, to[cnt_e] = v, cst[cnt_e] = c; }
int vis[maxn], onc[maxn], ar[maxn], sta[maxn], tp;
db ca[maxn], sz[maxn], szf[maxn];
void dfs0(int u, int ff) {
vis[u] = 1;
for (int i = info[u], v; i; i = Prev[i])
if (i ^ ff) {
v = to[i];
if (!vis[v]) {
sta[++tp] = i ^ 1;
dfs0(v, i ^ 1);
if (sta[tp] == i ^ 1)
tp--;
} else if (!onc[v]) {
ar[++ar[0]] = u, ca[ar[0]] = cst[i], onc[u] = 1;
for (int t = -1; to[t] != v;) {
t = sta[tp--];
ar[++ar[0]] = to[t], ca[ar[0]] = cst[t];
onc[to[t]] = 1;
}
per(i, ar[0], 1) sta[++tp] = ar[i];
}
}
}
void dfs1(int u, int ff) {
for (int i = info[u], v; i; i = Prev[i])
if ((v = to[i]) ^ ff && !onc[v])
dfs1(v, u), f[u] += f[v] + cst[i], sz[u]++;
if (sz[u])
f[u] /= sz[u];
}
void dfs2(int u, int ff) {
for (int i = info[u], v; i; i = Prev[i])
if ((v = to[i]) ^ ff && !onc[v]) {
g[v] = cst[i] + (sz[u] + szf[u] - 1 > 0
? (g[u] * szf[u] + f[u] * sz[u] - f[v] - cst[i]) / (sz[u] + szf[u] - 1)
: 0);
dfs2(v, u);
}
}
int main() {
scanf("%d%d", &n, &m);
rep(i, 1, m) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
Node(u, v, w), Node(v, u, w);
}
dfs0(1, 0);
rep(i, 1, n) szf[i] = 1 + (onc[i]);
if (ar[0] == 0)
ar[++ar[0]] = 1, szf[1] = 0;
rep(i, 1, ar[0]) dfs1(ar[i], 0);
rep(i, 1, ar[0]) {
db P = 0.5;
for (int j = i % ar[0] + 1; j != i; j = j % ar[0] + 1) {
g[ar[i]] += P * (ca[j] + (sz[ar[j]] + (j % ar[0] + 1 != i) > 0
? f[ar[j]] * sz[ar[j]] / (sz[ar[j]] + (j % ar[0] + 1 != i))
: 0));
P *= 1 / (sz[ar[j]] + 1);
}
P = 0.5;
for (int j = (i + ar[0] - 2) % ar[0] + 1; j != i; j = (j + ar[0] - 2) % ar[0] + 1) {
g[ar[i]] += P * (ca[j % ar[0] + 1] +
(sz[ar[j]] + ((j + ar[0] - 2) % ar[0] + 1 != i) > 0
? f[ar[j]] * sz[ar[j]] / (sz[ar[j]] + ((j + ar[0] - 2) % ar[0] + 1 != i))
: 0));
P *= 1 / (sz[ar[j]] + 1);
}
}
rep(i, 1, ar[0]) dfs2(ar[i], 0);
db ans = 0;
rep(i, 1, n) ans += (f[i] * sz[i] + g[i] * szf[i]) / (sz[i] + szf[i]);
ans /= n;
printf("%.5lf\n", ans);
}
HDU 3551 Hard Problem
有若干条边,从中选出若干条,判断是否有选边方案使得第 i i i个点的度数为 d i d_i di。
考虑将每个点的度数 d i d_i di拆分成 d i d_i di个点。
对于一条边
(
u
,
v
)
(u,v)
(u,v),
建点
u
e
,
v
e
ue,ve
ue,ve,
u
e
,
v
e
ue,ve
ue,ve间连边。
然后
u
e
ue
ue向所有
u
u
u拆出来的点连边,
v
e
ve
ve同理。
那么这样的一个图,
我们求他的最大匹配。
如果这个图的最大匹配是完美匹配。
那么如果
u
e
ue
ue匹配
v
e
ve
ve,说明这条边没被选中。
否则就是
u
e
ue
ue分走了
u
u
u的一个点度,
v
e
ve
ve同理。
完美匹配则度数都被处理完毕。
A C C o d e \mathcal AC \ Code AC Code
#include<bits/stdc++.h>
#define maxn 805
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
int n,m;
int sx[maxn],ty[maxn],d[maxn],tot,ue[maxn],ve[maxn];
int info[maxn],Prev[maxn*maxn],to[maxn*maxn],cnt_e;
void Node(int u,int v){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v; }
vector<int>G[maxn];
int mat[maxn],typ[maxn],F[maxn],nxt[maxn],q[maxn],ql,qr;
int Find(int u){ return !F[u] ? u : F[u] = Find(F[u]); }
int Lca(int u,int v){
static int vis[maxn]={},tim=0;
for(++tim;;swap(u,v)) if(u=Find(u))
if(vis[u] == tim) return u;
else vis[u] = tim , u = nxt[mat[u]];
}
void Blossom(int u,int v,int p){
for(;Find(u) != p;){
nxt[u] = v , v = mat[u];
if(typ[v] == 1) typ[v] = 2 , q[++qr] = v;
F[u] = F[v] = p , u = nxt[v];
}
}
int BFS(int u){
rep(i,1,tot) nxt[i] = F[i] = typ[i] = 0;
q[ql=qr=0] = u , typ[u] = 2;
for(;ql<=qr;){
u=q[ql++];
for(int j=info[u],v;j;j=Prev[j])
if(typ[v=to[j]] != 1 && Find(u) != Find(v)){
if(!typ[v]){
nxt[v] = u , typ[v] = 1;
if(mat[v]) q[++qr] = mat[v] , typ[mat[v]] = 2;
else{
for(int t;v;v=t) t=mat[mat[v]=nxt[v]],mat[mat[v]]=v;
return 1;
}
}
else{
int t = Lca(u,v);
Blossom(u,v,t) , Blossom(v,u,t);
}
}
}
return 0;
}
int main(){
int T,cas=0;
for(scanf("%d",&T);T--;){
scanf("%d%d",&n,&m);
tot = 0;
rep(i,1,m){
scanf("%d%d",&sx[i],&ty[i]);
ue[i] =++tot , ve[i] = ++tot;
Node(ue[i],ve[i]),Node(ve[i],ue[i]);
G[sx[i]].push_back(ue[i]);
G[ty[i]].push_back(ve[i]);
}
rep(i,1,n){
scanf("%d",&d[i]);
rep(j,1,d[i]){
++tot;
rep(k,0,G[i].size()-1)
Node(tot,G[i][k]),
Node(G[i][k],tot);
}
}
int ans = 0;
rep(i,1,tot) if(!mat[i]) ans += BFS(i);
printf("Case %d: ",++cas);
if(ans * 2 == tot) puts("YES");
else puts("NO");
rep(i,1,tot) info[i] = 0 , G[i].clear() , mat[i] = 0;
cnt_e = 0;
}
}
HDU 3311 Dig The Wells
给你n个寺庙,m个村庄,p条路,现在你要在这n+m个位置中选出若干个位置打井,每个位置打井的费用会告诉你,同时p条路也有修建费用,现在每个寺庙都住着一个和尚,问你最小的费用让这n个和尚都能喝上水。
斯坦纳树模板题。
#include<bits/stdc++.h>
#define maxn 2005
#define maxm 20005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
int n,m,p;
int info[maxn],Prev[maxm],to[maxm],cst[maxm],cnt_e;
void Node(int u,int v,int c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=c; }
int f[1<<5][maxn];
void Steiner_Tree(){
#define pii pair<int,int>
#define mp make_pair
priority_queue<pii,vector<pii >,greater<pii > >q;
memset(f,0x3f,sizeof f);
rep(i,1,n) f[1<<i-1][i] = f[0][i] = 0;
rep(i,n+1,n+m+1) f[0][i] = 0;
rep(i,0,(1<<n)-1){
rep(j,1,n+m+1){
for(int k=(i-1)&i;k;k=(k-1)&i)
f[i][j] = min(f[i][j] , f[k][j] + f[i^k][j]);
q.push(mp(f[i][j],j));
}
for(int u,w;!q.empty();){
u = q.top().second , w = q.top().first; q.pop();
if(w != f[i][u]) continue;
for(int k=info[u],v;k;k=Prev[k]) if(f[i][v=to[k]] > w + cst[k])
f[i][v] = w + cst[k] , q.push(mp(f[i][v],v));
}
}
printf("%d\n",f[(1<<n)-1][n+m+1]);
}
int main(){
while(~scanf("%d%d%d",&n,&m,&p)){
int w;
rep(i,1,n+m){
scanf("%d",&w);
Node(n+m+1,i,w);
Node(i,n+m+1,w);
}
rep(i,1,p){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
Node(u,v,w),Node(v,u,w);
}
Steiner_Tree();
rep(i,1,n+m+1)
info[i] = 0;
cnt_e = 0;
}
}
[WC2016]挑战NPC
n n n球入 m m m筐,每筐最多 3 3 3个,求最多有多少个筐装了 ≤ 1 \leq 1 ≤1个球,输出方案。
筐拆成3个点,三个点之间互相连边。
这样如果匹配了
≤
1
\leq 1
≤1个球,剩下的两个点就会自己匹配,
#include<bits/stdc++.h>
#define maxn 805
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
int n,m,E;
int info[maxn],Prev[maxn*maxn],to[maxn*maxn],cnt_e;
void Node(int u,int v){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v; }
int mat[maxn],nxt[maxn],F[maxn],q[maxn],typ[maxn],ql,qr;
int Find(int u){ return !F[u] ? u : F[u] = Find(F[u]); }
int Lca(int u,int v){
static int vis[maxn]={},tim=0;
for(++tim;;swap(u,v)) if(u=Find(u))
if(vis[u] == tim) return u;
else vis[u] = tim , u = nxt[mat[u]];
}
void Blossom(int u,int v,int p){
for(;Find(u) != p;){
nxt[u] = v , v = mat[u];
if(typ[v] == 1) typ[v] = 2 , q[++qr] = v;
F[u] = F[v] = p , u = nxt[v];
}
}
int BFS(int u){
rep(i,1,n+3*m) nxt[i] = typ[i] = F[i] = 0;
typ[q[ql=qr=0] = u] = 2;
for(;ql<=qr;){
u = q[ql++];
for(int i=info[u],v;i;i=Prev[i]) if(typ[v=to[i]] != 1 && Find(u) != Find(v)){
if(!typ[v]){
nxt[v] = u , typ[v] = 1;
if(!mat[v]){
for(int t;v;v=t) t=mat[mat[v] = nxt[v]] , mat[mat[v]] = v;
return 1;
}
typ[q[++qr] = mat[v]] = 2;
}
else{
int t = Lca(u,v);
Blossom(u,v,t),Blossom(v,u,t);
}
}
}
return 0;
}
int main(){
int T;
for(scanf("%d",&T);T--;){
scanf("%d%d%d",&n,&m,&E);
rep(i,1,m)
Node(i+2*m,i+m),Node(i+m,i+2*m);
rep(i,1,E){
int u,v;
scanf("%d%d",&u,&v);
Node(3*m+u,v),Node(v,3*m+u);
Node(3*m+u,v+m),Node(v+m,3*m+u);
Node(3*m+u,v+2*m),Node(v+2*m,3*m+u);
}
int ans = 0;
per(i,n+3*m,1) if(!mat[i])
ans += BFS(i);
printf("%d\n",ans - n);
rep(i,1,3*m+n) info[i]=0,mat[i]=0;
cnt_e=0;
}
}
「JLOI2015」管道连接
斯坦纳树+子集卷积。
A
C
C
o
d
e
\mathcal AC \ Code
AC Code
#include<bits/stdc++.h>
#define maxn 1005
#define maxm 6005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
int n,m,K,p[11],cnt[11];
int info[maxn],Prev[maxm],to[maxm],cst[maxm],cnt_e;
void Node(int u,int v,int c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=c; }
int f[1<<10][maxn],g[1<<10];
void Steiner_Tree(){
#define pii pair<int,int>
#define mp make_pair
priority_queue<pii,vector<pii >,greater<pii > >q;
rep(i,0,(1<<K)-1){
rep(j,1,n) for(int k=(i-1)&i;k;k=(k-1)&i)
f[i][j] = min(f[i][j] , f[k][j] + f[i^k][j]);
rep(j,1,n) q.push(mp(f[i][j],j));
for(int u,w;!q.empty();){
u=q.top().second,w=q.top().first;q.pop();
if(w != f[i][u]) continue;
for(int j=info[u],v;j;j=Prev[j]) if(f[i][v=to[j]] > f[i][u] + cst[j])
q.push(mp(f[i][v] = f[i][u] + cst[j] , v));
}
}
memset(g,0x3f,sizeof g);
rep(i,1,(1<<K)-1){
int c[11]={};
bool ER = 0;
rep(j,0,K-1) if(i>>j&1) c[p[j]]++;
rep(j,0,K-1) if(c[j] != 0 && c[j] != cnt[j]){
ER = 1;
break;
}
if(ER) continue;
rep(j,1,n) g[i] = min(g[i] , f[i][j]);
}
rep(i,1,(1<<K)-1)
for(int j=(i-1)&i;j;j=(j-1)&i)
g[i] = min(g[i] , g[j] + g[i-j]);
printf("%d\n",g[(1<<K)-1]);
}
int main(){
scanf("%d%d%d",&n,&m,&K);
rep(i,1,m){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
Node(u,v,w),Node(v,u,w);
}
memset(f,0x3f,sizeof f);
int x;rep(i,0,K-1) scanf("%d%d",&p[i],&x),f[1<<i][x] = 0,cnt[p[i]]++;
rep(i,1,n) f[0][i] = 0;
Steiner_Tree();
}
「THUSCH 2017」巧克力
随机化+二分答案+斯坦纳树
A C C o d e \mathcal AC \ Code AC Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define MAX 250
const int inf = 1e9;
inline int read() {
int x = 0;
bool t = false;
char ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-')
t = true, ch = getchar();
while (ch <= '9' && ch >= '0') x = x * 10 + ch - 48, ch = getchar();
return t ? -x : x;
}
int tr[MAX * MAX];
int n, m, K, c[MAX][MAX], a[MAX][MAX];
int f[1 << 5][MAX][MAX], val[MAX][MAX];
struct data {
int x, y;
};
queue<data> Q;
bool vis[MAX][MAX];
int d[4][2] = { 1, 0, -1, 0, 0, 1, 0, -1 };
void SPFA(int S, int V) {
while (!Q.empty()) {
int x = Q.front().x, y = Q.front().y;
Q.pop();
for (int i = 0; i < 4; ++i) {
int xx = x + d[i][0], yy = y + d[i][1];
if (xx < 1 || yy < 1 || xx > n || yy > m || c[xx][yy] == -1)
continue;
int p = f[S][x][y] + val[xx][yy];
if (p < f[S][xx][yy]) {
f[S][xx][yy] = p;
if (!vis[xx][yy])
vis[xx][yy] = true, Q.push((data){ xx, yy });
}
}
vis[x][y] = false;
}
}
int DP(int V) {
for (int S = 0; S < (1 << K); ++S)
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) f[S][i][j] = inf;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (c[i][j] != -1)
f[1 << tr[c[i][j]]][i][j] = val[i][j];
for (int S = 0; S < 1 << K; ++S) {
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
if (c[i][j] == -1)
continue;
for (int t = (S - 1) & S; t; t = (t - 1) & S)
f[S][i][j] = min(f[S][i][j], f[t][i][j] + f[S ^ t][i][j] - val[i][j]);
if (f[S][i][j] <= inf)
Q.push((data){ i, j }), vis[i][j] = true;
}
SPFA(S, V);
}
int mn = inf;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) mn = min(mn, f[(1 << K) - 1][i][j]);
return mn;
}
int St[MAX * MAX], top;
void Work() {
n = read();
m = read();
K = read();
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) c[i][j] = read();
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) a[i][j] = St[++top] = read();
sort(&St[1], &St[top + 1]);
top = unique(&St[1], &St[top + 1]) - St - 1;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) a[i][j] = lower_bound(&St[1], &St[top + 1], a[i][j]) - St;
int l = 1, r = top, ret = r + 1, blk = n * m + 1;
while (l <= r) {
int mid = (l + r) >> 1, mn = inf;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) val[i][j] = (a[i][j] > mid) ? 1001 : 1000;
for (int Tim = 200; Tim; --Tim) {
for (int i = 1; i <= n * m; ++i) tr[i] = rand() % K;
mn = min(mn, DP(mid));
if (blk > n * m || mn / 1000 != blk)
continue;
int b = mn / 1000, c = mn % 1000;
if (b - c >= (b + 1) / 2)
break;
}
if (mn >= inf) {
puts("-1 -1");
return;
}
blk = mn / 1000;
int cnt = mn % 1000;
if (blk - cnt >= (blk + 1) / 2)
r = mid - 1, ret = mid;
else
l = mid + 1;
}
printf("%d %d\n", blk, St[ret]);
}
int main() {
srand(time(NULL));
int T = read();
while (T--) Work();
return 0;
}