Codeforces Round #572 (Div. 1)
A1:
观察发现,只要有度为2的点,就会导致这个点关联的两条边的权重相等,此时为NO,否则是YES。
#include
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
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
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
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;
}