A. Powered Addition
略
B. Edge Weight Assignment
略
C. Perfect Triples
找找规律容易发现答案跟四进制相关。
考虑将数字写成四进制,容易观察并归纳证明:所有匹配的
(
a
,
b
,
c
)
(a,b,c)
(a,b,c)位数都相同,并且位数相同,最高位为
1
1
1,
2
2
2和
3
3
3的数字分别会作为
a
a
a,
b
b
b和
c
c
c出现,互相匹配,且
a
a
a从小到大取遍所有最高位为
1
1
1的数,而后面每一位匹配关系是独立的,具体的,匹配关系是
(
0
,
0
,
0
)
(0,0,0)
(0,0,0),
(
1
,
2
,
3
)
(1,2,3)
(1,2,3),
(
2
,
3
,
1
)
(2,3,1)
(2,3,1),
(
3
,
1
,
2
)
(3,1,2)
(3,1,2)。那么将
n
n
n写成四进制即可反过来得到
s
n
s_n
sn了。
时间复杂度
O
(
t
log
n
)
\mathcal O(t\log n)
O(tlogn)。
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int A[4]={0,1,2,3};
const int B[4]={0,2,3,1};
const int C[4]={0,3,1,2};
int main() {
int cases;
scanf("%d",&cases);
for(;cases;cases--) {
ll n;
scanf("%lld",&n);
if (n<=3) {
printf("%lld\n",n);
continue;
}
n-=4;
int v=n%3;
n/=3;
ll d=1;
int cnt=0;
for(;;) {
d*=4;
cnt++;
if (d>n) break;
n-=d;
}
int val[60],sz=0;
memset(val,0,sizeof(val));
while (n) {
val[++sz]=n%4;
n/=4;
}
ll s=0;
if (!v) {
s+=1;
for(int i=cnt;i>0;i--) s=s*4LL+A[val[i]];
}
if (v==1) {
s+=2;
for(int i=cnt;i>0;i--) s=s*4LL+B[val[i]];
}
if (v==2) {
s+=3;
for(int i=cnt;i>0;i--) s=s*4LL+C[val[i]];
}
printf("%lld\n",s);
}
return 0;
}
/*
3
7
8
9
*/
D. Nested Rubber Bands
考虑钦定一个点集
S
S
S,如何判定
S
S
S能否作为一个链出现。
首先注意到
S
S
S中不可能包含相邻的点。显然只用考虑
S
S
S中点构成的虚树上的点,对于虚树上某个非叶子节点
x
x
x,如果
x
x
x出现在
S
S
S中,那么容易证明合法必须满足以
x
x
x为根的话,至多两个子树中有
S
S
S中的点;如果
x
x
x没有出现在
S
S
S中,那么必须满足以
x
x
x为根的话,至多两个子树包含
S
S
S中的点且不是子树中仅有与
x
x
x相邻的点在
S
S
S中。
这样令
F
[
i
]
[
0
/
1
]
[
0
/
1
/
2
]
F[i][0/1][0/1/2]
F[i][0/1][0/1/2]表示考虑点
i
i
i子树,点
i
i
i是否选择,
i
i
i儿子子树中有几个包含
S
S
S中的点且不是子树中仅有与
i
i
i相邻的点在
S
S
S中,简单树形DP一下即可。
时间复杂度
O
(
n
)
\mathcal O(n)
O(n)。
#include <bits/stdc++.h>
using namespace std;
vector <int> e[100005];
int f[100005][2][3],ans;
void dfs(int x,int fa) {
static int g[2][3];
f[x][0][0]=0;
f[x][1][0]=1;
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa) {
int u=e[x][i];
dfs(u,x);
memcpy(g,f[x],sizeof(g));
for(int j=0;j<3;j++) {
if (f[x][0][j]!=-1) {
g[0][j]=max(g[0][j],f[x][0][j]+1);
for(int k=0;k<2&&j+1<3;k++) {
int t=max(f[u][0][k],f[u][1][k]);
if (t!=-1) g[0][j+1]=max(g[0][j+1],f[x][0][j]+t);
}
}
if (f[x][1][j]!=-1) {
for(int k=0;k<2&&j+1<3;k++) {
int t=f[u][0][k];
if (t!=-1) g[1][j+1]=max(g[1][j+1],f[x][1][j]+t);
}
}
}
memcpy(f[x],g,sizeof(f[x]));
}
for(int i=0;i<3;i++) {
ans=max(ans,f[x][0][i]);
ans=max(ans,f[x][1][i]);
if (fa) ans=max(ans,f[x][0][i]+1);
}
}
int main() {
memset(f,255,sizeof(f));
int n;
scanf("%d",&n);
for(int i=1;i<n;i++) {
int x,y;
scanf("%d%d",&x,&y);
e[x].push_back(y);
e[y].push_back(x);
}
dfs(1,0);
printf("%d\n",ans);
return 0;
}
E. JYPnation
给定的图是一个竞赛图。众所周知,竞赛图缩点后形成一条链,且取一个生成子图,若存在环必然存在三元环。显然根据题意,只可能有最后一个强连通分量大小
>
1
>1
>1。那么不断删去
0
0
0度点,只考虑一个非平凡强连通分量的情况。
考虑枚举点
x
x
x计算所有
d
i
s
(
x
,
y
)
dis(x,y)
dis(x,y),那么可以发现
∀
y
\forall y
∀y,
d
i
s
(
x
,
y
)
≤
3
dis(x,y)\leq 3
dis(x,y)≤3,证明是考虑若最短路长度
>
3
>3
>3,注意到最短路上不相邻的节点一定由后面连向前面,那么取出最短路上最后三个点及
x
x
x就非法了。
d
i
s
(
x
,
i
)
=
1
dis(x,i)=1
dis(x,i)=1显然当且仅当
x
x
x到
i
i
i有边,令
d
i
s
(
x
,
i
)
=
1
dis(x,i)=1
dis(x,i)=1的集合为
S
S
S,
d
i
s
(
x
,
i
)
>
1
dis(x,i)>1
dis(x,i)>1的集合为
T
T
T,显然
S
S
S和
T
T
T都非空。
考虑
T
T
T集合中一个点
y
y
y,使得
d
i
s
(
x
,
y
)
=
2
dis(x,y)=2
dis(x,y)=2,那么令
S
S
S中到
y
y
y有边的集合为
U
U
U,其他点集合为
V
V
V,显然
U
U
U非空。首先容易证明
U
U
U中无环,其次可以证明
U
U
U和
V
V
V连接的边都是由
V
V
V连向
U
U
U(否则若存在
p
∈
U
p\in U
p∈U,
q
∈
V
q\in V
q∈V,由
p
p
p连向
q
q
q,那么取出
x
x
x,
p
p
p,
y
y
y和
q
q
q就非法了),还能证明
V
V
V中也无环(否则必存在三元环,且全部连向至少一个
U
U
U中的点,显然非法)。
这样,可以发现
S
S
S内部形成一条链,且连向
y
y
y的点是这条链的一个后缀。事实上,甚至可以证明
T
T
T内部也形成一条链,且
d
i
s
(
x
,
y
)
=
3
dis(x,y)=3
dis(x,y)=3的点是这条链的一个后缀,并且从前往后考虑链上每个点,
S
S
S到它的边数单调不升。
有了上面的引理就很好做了。考虑先求出
S
S
S内部最后一个点
m
x
mx
mx,因为
S
S
S内部形成一条链,那么直接看两个点之间边的方向就可以判定在链上的相对位置关系,所以扫一遍即可。然后对于
T
T
T中每个点
y
y
y,
d
i
s
(
x
,
y
)
=
2
dis(x,y)=2
dis(x,y)=2当且仅当
m
x
mx
mx到
y
y
y有边。
时间复杂度
O
(
n
2
)
\mathcal O(n^2)
O(n2)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool e[8005][8005];
int ind[8005];
bool vis[8005];
char str[8005];
int main() {
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%s",str+1);
int r=0;
for(int j=1;j<=n;j+=4) {
r++;
int t=(isdigit(str[r]))?str[r]-'0':str[r]-'A'+10;
e[i][j]=((t>>3)&1);
e[i][j+1]=((t>>2)&1);
e[i][j+2]=((t>>1)&1);
e[i][j+3]=(t&1);
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if (e[i][j]) ind[j]++;
ll ans=0;
int sz=n;
for(;;) {
int id=0;
for(int i=1;i<=n;i++)
if (!vis[i]&&!ind[i]) {
id=i;
break;
}
if (!id) break;
sz--;
vis[id]=1;
ans+=614LL*n*sz+sz;
for(int i=1;i<=n;i++)
if (e[id][i]) ind[i]--;
}
for(int i=1;i<=n;i++)
if (!vis[i]) {
int cnt=0,id=0;
for(int j=1;j<=n;j++)
if (!vis[j]&&e[i][j]) {
cnt++;
if (!id||e[id][j]) id=j;
}
ans+=cnt;
for(int j=1;j<=n;j++)
if (!vis[j]&&i!=j&&!e[i][j]) ans+=((e[id][j])?2:3);
}
printf("%lld\n",ans);
return 0;
}