Black Magic 分类讨论
链接:Black Magic
题意:
给出 n n n个方块,每个方块的左/右都可能是黑或白。将这些方块排成一列,如果两个相邻方块相连接的面都是黑色,那么这两个方块会连在一起。求连通块的最大/最小数量。 0 ≤ n ≤ 1 0 5 T ( 1 ≤ T ≤ 4 × 1 0 3 ) 0≤n≤10^5 ~~~~~T (1≤T≤4×10^3) 0≤n≤105 T(1≤T≤4×103)
分析:
最小数量:要尽可能的将
L
,
R
L,R
L,R凑成一对,对于凑不成的只能单独一个,因此直接找
m
a
x
(
L
,
R
)
max(L,R)
max(L,R)即可,
B
B
B可以放在
L
/
R
L/R
L/R的旁边就能形成一个。
最大数量:
L
L
L
L
R
R
R
R
LLLLRRRR
LLLLRRRR这样排列每个
L
L
L和
R
R
R都是独立的,如果有
B
B
B的话,可以选一个
B
B
B放在
L
L
L与
R
R
R的交界处,剩下的
B
B
B只能与
E
E
E交错着放。
#include<iostream>
using namespace std;
typedef long long ll;
void solve()
{
int e,l,r,b;
cin>>e>>l>>r>>b;
int t=max(l,r);
int minn;
if(t==0)
minn=e+min(1,b);
else
minn=t+e;
int maxn;
if(e==0)
maxn=l+r+min(1,b);
else
{
if(b<=2)
maxn=l+r+b+e;
else
{
e--;
b-=2;
maxn=l+r+3;
if(b>e) maxn+=e*2;
else maxn+=b+e;
}
}
cout<<minn<<" "<<maxn<<endl;
}
signed main()
{
int t;
t=1;
cin>>t;
for(int i=1;i<=t;i++)
{
solve();
}
return 0;
}
Triangle Game 博弈论
题意:
给一个合法的三角形的三边长度,你和另一个人轮流让三角形的某边递减且仍然是三角形,如果某个人无法继续操作了就输了,你先手。 T ( 1 ≤ T ≤ 1 0 4 ) a , b , c ( 1 ≤ a , b , c ≤ 1 0 9 ) T (1≤T≤10^4) a,b,c (1≤a,b,c≤10^9) T(1≤T≤104)a,b,c(1≤a,b,c≤109)
分析:
这是一个NIM游戏的变形博弈论。
规律: 只有在
(
a
−
1
)
(a-1)
(a−1) ^
(
b
−
1
)
(b-1)
(b−1) ^
(
c
−
1
)
!
=
0
(c-1)!=0
(c−1)!=0的时候,先手必胜。
证明: 对任意的三角形三边
a
,
b
,
c
a,b,c
a,b,c,这里不妨假设
a
>
=
b
>
=
c
a>=b>=c
a>=b>=c,有
b
+
c
>
a
b+c>a
b+c>a,
变形有
b
−
1
+
c
−
1
>
=
a
−
1
b-1 + c-1 >=a-1
b−1+c−1>=a−1
为了方便,用
a
b
c
abc
abc取代公式里的
a
−
1
,
b
−
1
,
c
−
1
a-1,b-1,c-1
a−1,b−1,c−1,即
b
+
c
>
=
a
b+c>=a
b+c>=a
假设
a
a
a ^
b
b
b ^
c
!
=
0
c!=0
c!=0
那么一定存在一个数
x
x
x,使得
a
a
a ^
(
b
−
x
)
(b-x)
(b−x) ^
c
=
0
c=0
c=0,即
b
−
x
=
a
b-x=a
b−x=a ^
c
c
c
又因为
a
+
c
>
=
a
a+c>=a
a+c>=a ^
c
>
=
a
−
c
c>=a-c
c>=a−c(这就是为什么一开始要-1,取到>=的原因)
就有
a
+
c
>
=
b
−
x
>
=
a
−
c
a+c>=b-x>=a-c
a+c>=b−x>=a−c
可以发现在减掉一个
x
x
x之后三条边仍然满足三角形的性质且
a
a
a ^
(
b
−
x
)
(b-x)
(b−x) ^
c
=
0
c=0
c=0,
这里证明的是
b
+
c
>
a
b+c>a
b+c>a,那么
a
+
c
>
b
a+c>b
a+c>b和
a
+
b
>
c
a+b>c
a+b>c也是类似的证明
那么留给对手的就是一个异或和等于0的状态,对手只会产生两种结果:
1.无法操作,输掉游戏
2.操作到异或和不等于
0
0
0的状态,那么我们就可以重复上面的操作,最终对方会输。
因此在
(
a
−
1
)
(a-1)
(a−1) ^
(
b
−
1
)
(b-1)
(b−1) ^
(
c
−
1
)
!
=
0
(c-1)!=0
(c−1)!=0是一个必胜状态。
#include<iostream>
#include<cstring>
#include<algorithm>
//#define int long long
using namespace std;
typedef long long ll;
void solve()
{
int a,b,c;
cin>>a>>b>>c;
if((a-1)^(b-1)^(c-1)) puts("Win");
else puts("Lose");
}
signed main()
{
int t;
t=1;
cin>>t;
for(int i=1;i<=t;i++)
{
solve();
}
return 0;
}
Counting Stickmen 组合数学
题意:
给你一棵树,求这棵树上形成的火柴人的形状有多少个,只要两个火柴人存在两个点不一样,就认为这两个火柴人是不同的。
T
(
1
≤
T
≤
15
)
n
(
1
≤
n
≤
5
×
1
0
5
)
T (1≤T≤15) n (1≤n≤5×10^5)
T(1≤T≤15)n(1≤n≤5×105)
图中红色的就是一个火柴人,
3
3
3是身子,
2
2
2是头,
5
,
7
,
8
5,7,8
5,7,8是下半身,
4
,
6
4,6
4,6和
9
,
10
9,10
9,10是俩胳膊
分析:
只要找出来火柴人的核心即可,方法有很多。相对容易的就是找连接头,胳膊和腿的点为核心,例如上图中的3号点,只需要预处理出来每个点作为核心点所形成的胳膊数量即可,头的个数很好找(3号点的邻接点数量-3),在枚举每条边的时候会枚举出来核心点和腿,最后用乘法原理就能实现。注意要减去腿对核心点形成胳膊的贡献。
#include<iostream>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
typedef long long ll;
const int N=5e5+10,M=2*N,mod=998244353;
int cnt[N],ng[N],ngc[N];
int e[M],ne[M],h[N],idx;
int n;
struct Edge
{
int a,b;
}edge[N];
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
int res;
int calc(int x)
{
return x*(x-1)/2%mod;
}
void solve()
{
idx=res=0;
cin>>n;
for(int i=1;i<=n;i++)
h[i]=-1,cnt[i]=0,ng[i]=0,ngc[i]=0;
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d %d",&a,&b);
edge[i]={a,b};
add(a,b);
add(b,a);
cnt[a]++;
cnt[b]++;
}
for(int i=1;i<=n;i++)
{
for(int j=h[i];j!=-1;j=ne[j])
{
int u=e[j];
ng[i]+=cnt[u]-1;
ngc[i]=(ngc[i]+calc(cnt[u]-1))%mod;
}
//cout<<i<<" "<<ng[i]<<" "<<ngc[i]<<endl;
}
for(int i=1;i<n;i++)
{
int x=edge[i].a,y=edge[i].b;
int foot=calc(cnt[y]-1);
int hand=calc(ng[x]-cnt[y]+1);
hand=hand-ngc[x]+calc(cnt[y]-1);
int head=cnt[x]-3;
// if(hand<0||head<0||foot<0);
// else
res=(res+head*hand%mod*foot%mod)%mod;
// cout<<x<<" "<<y<<"hand"<<hand<<"head"<<head<<"foot"<<foot<<endl;
x=edge[i].b,y=edge[i].a;
foot=calc(cnt[y]-1);
hand=calc(ng[x]-cnt[y]+1);
hand=hand-ngc[x]+calc(cnt[y]-1);
head=cnt[x]-3;
// if(hand<0||head<0||foot<0);
// else
res=(res+head*hand%mod*foot%mod)%mod;
// cout<<x<<" "<<y<<"hand"<<hand<<"head"<<head<<"foot"<<foot<<endl;
}
cout<<res<<endl;
}
signed main()
{
int t;
t=1;
cin>>t;
for(int i=1;i<=t;i++)
{
solve();
}
return 0;
}
Independent Feedback Vertex Set 三染色
链接:Independent Feedback Vertex Set
题意:
给一个图有 n n n个点,每个点都有权值,图初始是由三个顶点组成 1 , 2 , 3 1,2,3 1,2,3和三个边 ( 1 , 2 ) , ( 2 , 3 ) , ( 3 , 1 ) (1,2), (2,3), (3,1) (1,2),(2,3),(3,1)组成的一个三元环,之后会再给出 n − 3 n-3 n−3行,每行给出两个点 u , v u,v u,v,表示连接 ( i + 3 , u ) (i+3,u) (i+3,u)和 ( i + 3 , v ) (i+3,v) (i+3,v)的两条边,保证给出的 u , v u,v u,v是有边的。给出图之后,让你将这个图分成森林和独立集,且独立集中顶点的权值之后最大。
分析:
可以发现这个图是由许多三元环构成的,要想变成森林还要构成独立集的话,那么每个环必须且只能分出一个点。
因为一个点都不选则会破坏森林约束,选两个则会破坏独立集约束。
可以发现,在初始的三元环中选出一个点的话,那么后面选出的点是唯一且固定的,也就是说该图的三染色方案是唯一的,最后找三染色图的哪个色的权值和最大即可。
#include<iostream>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
typedef long long ll;
const int N=1e5+10,M=2*N,mod=998244353;
int w[N],color[N];
int n;
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
{
color[i]=0;
scanf("%lld",&w[i]);
}
for(int i=1;i<=3;i++) color[i]=i;
for(int i=4;i<=n;i++)
{
int a,b;
scanf("%lld %lld",&a,&b);
color[i]=6-color[a]-color[b];
}
int res[4]={0,0,0,0};
for(int i=1;i<=n;i++)
res[color[i]]+=w[i];
cout<<max(res[1],max(res[2],res[3]))<<endl;
}
signed main()
{
int t;
t=1;
cin>>t;
for(int i=1;i<=t;i++)
{
solve();
}
return 0;
}