Codeforces Round #572 (Div. 1)
A1:
观察发现,只要有度为2的点,就会导致这个点关联的两条边的权重相等,此时为NO,否则是YES。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
vector<int> adj[N];
int n;
bool ok;
void dfs(int u, int p) {
if(p!=0&&adj[u].size()==2) ok=false;
for(int v : adj[u]) {
if(v==p) continue;
dfs(v, u);
}
}
int main() {
scanf("%d", &n);
for(int i=1; i<n; ++i) {
int u, v;
scanf("%d%d", &u, &v);
adj[u].push_back(v);
adj[v].push_back(u);
}
ok = true;
for(int i=1; i<=n; ++i) {
if(adj[i].size()==1) {
dfs(i, 0);
break;
}
}
if(ok) puts("YES");
else puts("NO");
}
A2:
首先观察可以发现,对于一个点
p
1
p_1
p1 ,它的两个儿子是
p
2
p_2
p2 和
p
3
p_3
p3 ,它的父亲是
p
4
p_4
p4 ,且
p
2
p_2
p2 和
p
3
p_3
p3 是叶子,设
(
p
1
,
p
4
)
(p_1, p_4)
(p1,p4) 这条边为
x
x
x ,值为
w
x
w_x
wx ,设
(
p
1
,
p
2
)
(p_1, p_2)
(p1,p2) 这条边为
y
y
y ,值为
w
y
w_y
wy,设
(
p
1
,
p
3
)
(p_1,p_3)
(p1,p3) 这条边为
z
z
z,值为
w
z
w_z
wz 。那么我们总可以通过
p
2
p_2
p2 ,
p
3
p_3
p3 和根
r
t
rt
rt (假设我们从一个度为
1
1
1 的点开始dfs的)这三个叶子将
x
x
x 和
y
y
y 的权重变为
w
y
w_y
wy ,将
z
z
z 的权重变为
0
0
0。那么接下来我们可以对每个儿子进行上述操作,最后得到一个值为
w
y
w_y
wy 的儿子边和其它值为
0
0
0 的儿子边。然后我们回溯到父亲,由于之前的
x
x
x 和
y
y
y 的权重是相等的,因此我们可以把
(
p
2
,
p
4
)
(p_2,p_4)
(p2,p4) 这条路径看做一条权重为
w
y
w_y
wy 的边。然后对于它的每个儿子,同样经过dfs后也得到一条被边权相等的路径。因此此时又可以看做得到一条有边权相等的路径加若干条权重为
0
0
0 的路径。一直到根,得到一条有权重的路径,最后我们将这条边权相等的路径清零即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 1007;
struct Edge {
int u, v, x;
};
vector<Edge> ans;
vector<Edge> adj[N];
int c[N]; //底层边剩余的权重。
int lf[N]; //底层边对应的叶子
int rt;
bool ok;
void check(int u, int p) {
if(p!=0&&adj[u].size()==2) ok=false;
for(Edge e : adj[u]) {
int v = e.v;
if(v==p) continue;
check(v, u);
}
}
int dfs(int u, int p, int w) {
// w是上面那条边的原始值
// diff:返回值是上层边需增加的值
int diff = 0;
// printf("dfs: %d\n", u);
if(adj[u].size()==1) {
lf[u] = u;
c[u] = w;
return 0;
}
bool first = true;
int fu;
for(Edge e : adj[u]) {
if(e.v == p) continue;
int v = e.v;
int d = dfs(v, u, e.x);
diff += d;
w += d;
if(first) fu = v;
else {
// printf("process %d %d, traingle=%d %d %d\n", u, v, w, c[fu], c[v]);
int origin_cfu = c[fu];
int target = (w+c[fu]-c[v])/2;
ans.push_back({rt, lf[v], target-w});
ans.push_back({lf[fu], lf[v], target-c[fu]});
diff += target-w;
w=c[fu]=target;
ans.push_back({lf[fu], rt, origin_cfu-c[fu]});
diff += origin_cfu-c[fu];
c[fu]=w=origin_cfu;
}
first = false;
}
c[u]=w;
lf[u] = lf[fu];
return diff;
}
int main() {
int n;
scanf("%d", &n);
for(int i=1; i<n; ++i) {
int u, v, x;
scanf("%d%d%d", &u, &v, &x);
adj[u].push_back({u, v, x});
adj[v].push_back({v, u, x});
}
ok = true;
for(int i=1; i<=n; ++i) {
if(adj[i].size()==1) {
check(i, 0);
break;
}
}
if(!ok) {
puts("NO");
exit(0);
}
for(int i=1; i<=n; ++i) {
if(adj[i].size()==1) {
rt=i;
dfs(adj[i][0].v, i, adj[i][0].x);
ans.push_back({i, lf[adj[i][0].v], -c[adj[i][0].v]});
break;
}
}
puts("YES");
printf("%d\n", (int)ans.size());
for(Edge e : ans){
printf("%d %d %d\n", e.u, e.v, -e.x);
}
return 0;
}
B:
找出所有
(
a
i
+
a
j
)
(
a
i
2
+
a
j
2
)
≡
k
 
m
o
d
 
p
(a_i + a_j)(a_i^2 + a_j^2) \equiv k \bmod p
(ai+aj)(ai2+aj2)≡kmodp 条件满足的
(
i
,
j
)
(i,j)
(i,j) 数量。因为与
i
i
i ,
j
j
j 相关的变量在等式的同一边,因此无法通过存在 set
里进行维护。两边乘上
(
a
i
−
a
j
)
(a_i-a_j)
(ai−aj) 后得到
a
i
4
−
k
a
i
=
a
j
4
−
k
a
j
a_i^4-ka_i=a_j^4-ka_j
ai4−kai=aj4−kaj 此时
i
i
i 和
j
j
j 相关的变量在等式两边了,因此可以通过将已遍历过的
j
j
j 存在 set
里维护即可。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
int n;
ll p, k;
scanf("%d%I64d%I64d", &n, &p, &k);
ll ans = 0;
multiset<ll> s;
for(int i=0; i<n; ++i) {
ll a;
scanf("%I64d", &a);
ll res = 0;
res =(p-k*a%p)%p;
a=a*a%p;
a=a*a%p;
res = (res+a)%p;
// printf("pp %I64d\n", res);
ans += s.count(res);
s.insert(res);
}
printf("%I64d\n", ans);
}
F:
首先想到可以通过算出每个美丽值对应的序列数量,进一步可以发现,设美丽值大于等于
x
x
x 的序列数量为
p
x
p_x
px ,设
a
a
a 数组的最大值为
m
a
x
(
a
)
max(a)
max(a) ,那么答案是
p
1
+
p
2
+
.
.
.
+
p
m
a
x
(
a
)
p_1+p_2+...+p_{max(a)}
p1+p2+...+pmax(a) 。我们可以将数组排序后设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 为
i
i
i 位置,选
j
j
j 个作为序列元素的满足条件的序列数量,设
a
l
a_l
al 是满足
a
i
−
a
l
≥
p
x
a_i-a_l \geq p_x
ai−al≥px 的第一个值,那么答案从
a
1
.
.
.
a
l
a_1...a_l
a1...al 的dp值转移而来,因为随着
x
x
x 的递增
a
l
a_l
al 是单调减的,因此维护一个指针就可以实现
O
(
1
)
O(1)
O(1) 的转移。因此dp的复杂度是
O
(
n
k
)
O(nk)
O(nk) 。
进一步观察可以发现,美丽值是不能超过
m
a
x
(
a
)
(
k
−
1
)
\frac{max(a)}{(k-1)}
(k−1)max(a) 的,因为有
(
k
−
1
)
(k-1)
(k−1) 个不同的差。如果超过这个值,序列的元素数量肯定小于
k
k
k ,这种情况不合法。因此总的复杂度为
O
(
m
a
x
(
a
)
k
−
1
n
k
)
O(\frac{max(a)}{k-1}nk)
O(k−1max(a)nk) 。即
O
(
m
a
x
(
a
)
n
)
O(max(a)n)
O(max(a)n) 。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll mod = 998244353;
const int N = 1e3+7;
int a[N], lb[N];
ll dp[N][N];
// dp[i][j]=1~i中长度为j的合法序列数量
int main() {
int n, k;
scanf("%d%d", &n, &k);
int mx=0;
for(int i=1; i<=n; ++i) {
scanf("%d", &a[i]);
mx = max(mx, a[i]);
lb[i] = i;
}
mx /= (k-1);
// printf("mx: %d\n", mx);
sort(a+1, a+n+1);
ll ans = 0;
for(int p=1; p<=mx; ++p) {
ll res = 0;
for(int i=1; i<=n; ++i) dp[i][1] = i;
for(int i=1; i<=n; ++i) {
while(lb[i]>0&&a[i]-a[lb[i]]<p) --lb[i];
for(int j=k; j>=2; --j) {
dp[i][j] = dp[lb[i]][j-1] + dp[i-1][j];
dp[i][j] %= mod;
// printf("p=%d, lb=%d dp[%d][%d]=%I64d\n", p, lb[i], i, j, dp[i][j]);
}
}
ans = (ans+dp[n][k])%mod;
}
printf("%I64d\n", ans);
return 0;
}