AtCoder Beginner Contest 220
D - FG operation
題目
有\(n\)個數,每次可以進行兩種操作:
- 取出前兩個數,相加後對\(10\)取模並將結果放入數列最左端
- 取出前兩個數,相乘後對\(10\)取模並將結果放入數列最左端
問,有多少種方案,使得最終結果等於\(k\),\(k\)取0到9的整數.
思路
明顯的DP,設\(f_{i,j}\)
\(f_{i+1,j\cdot a_i \%10}=f_{i+1,j\cdot a_i \%10}+f_{i,j}\)
\(f_{i+1,(j+a_i)\%10}=f_{i+1,(j+a_i)\%10}+f_{i,j}\)
#include <iostream> #include <cstdio> #include <algorithm> #include <string> #define int long long using namespace std; int read() { int re = 0; char c = getchar(); bool negt = false; while(c < '0' || c > '9') negt |= (c == '-') , c = getchar(); while(c >= '0' && c <= '9') re = (re << 1) + (re << 3) + c - '0' , c = getchar(); return negt ? -re : re; } const int mod = 998244353; const int N = 100010; int n , a[N]; int f[N][12]; signed main() { n = read(); for(int i = 1 ; i <= n ; i++) a[i] = read(); ++f[2][(a[1] + a[2]) % 10]; ++f[2][(a[1] * a[2]) % 10]; for(int i = 2 ; i < n ; i++) for(int j = 0 ; j < 10 ; j++) { f[i + 1][(j + a[i + 1]) % 10] += f[i][j] , f[i + 1][(j + a[i + 1]) % 10] %= mod; f[i + 1][(j * a[i + 1]) % 10] += f[i][j] , f[i + 1][(j * a[i + 1]) % 10] %= mod; } for(int i = 0 ; i < 10 ; i++) cout << f[n][i] << endl; return 0; }
E - Distance on Large Perfect Binary Tree
題目
思路
應該是前六題中最難的一道題.
看下資料範圍,大概是\(O(n)\)的.
所以,我們可以一層一層算,設第\(i\)層的一個點對答案的貢獻為\(f(i)\)則答案就是\(\sum^n_{i=1}f(i)\cdot 2^{i-1}\).
問題就是計算\(f(i)\).
分三種情況:直接向下,直接向上,先向上後向下.
對於第一種情況,如果\(i\)到二叉樹底的距離足夠大,產生的貢獻就是\(2^{d}\),否則沒有貢獻(可以到達以綠框二叉樹的任意一個葉子).
對於第二種情況,如果\(i\)的深度足夠大,產生的貢獻就是\(1\)
//預處理2的次方.
p[0] = 1;
for(int i = 1 ; i <= n ; i++)
p[i] = (p[i - 1] << 1) % mod;
//在for迴圈中.
if(n - i >= d)
tmp += p[d];
if(i > d)
++tmp;
對於第三種情況:
如果到樹底的距離足夠大,我們可以向上一步然後向下,貢獻就是\(2^{h-1}=2^{d-2}\).
如果深度足夠大,貢獻就是\(2^{h-1}=1\).
除外,我們還可以向上走\(2\)步,\(3\)步\(\cdots\)再向下走.
貢獻就是\(\sum^{d-2}_{i=0}2^i\).
那如果距離不夠大呢?
對於右圖,容易得到,\(d - i < 0\)時,深度足夠,否則,綠框二叉樹的深度最小值為\(d - i+1\),貢獻是\(2^{d-i}\).
對於左圖,我們設向上走到\(j\)深度再向下走可以滿足條件,則有\((i-j)+(n-j)\ge d\),即\(j\le \frac{i+n-d}2\),\(j\)的最大值就是\(\lfloor \frac{i+n-d}2\rfloor\),綠框二叉樹的深度也就是\(d-(i-j)\).
所以我們定義一個\(l,r\).
int l = (d - i < 0 ? 0 : d - i);
int r = (n - i >= d - 2 ? d - 2 : d - (i - (i + n - d) / 2)) - 1;
答案就是\(\sum^{r}_{i=1}2^i\).根據等比數列,答案就是
\[2^{r+1}-2^l \]程式碼
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#define int long long
using namespace std;
int read() {
int re = 0;
char c = getchar();
bool negt = false;
while(c < '0' || c > '9')
negt |= (c == '-') , c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return negt ? -re : re;
}
const int mod = 998244353;
int p[1000010];
int n , d;
signed main() {
n = read() , d = read();
p[0] = 1;
for(int i = 1 ; i <= n ; i++)
p[i] = (p[i - 1] << 1) % mod;
int ans = 0;
for(int i = 1 ; i <= n ; i++) {
int tmp = 0;
if(n - i >= d)
tmp += p[d];
if(i > d)
++tmp;
int l = (d - i < 0 ? 0 : d - i);
int r = (n - i >= d - 2 ? d - 2 : d - 1 - (i - (i + n - d) / 2));
if(l <= r) {
tmp += (p[r + 1] - p[l] + mod) % mod;
}
tmp %= mod;
ans = (ans + tmp * p[i - 1]) % mod;
// printf("%lld:\t%lld\n" , i , tmp);
}
cout << ans % mod;
return 0;
}
F - Distance Sums 2
題目
思路
簡單的樹形DP,設\(f_i\)表示其他點到\(i\)點的距離之和.
則有:
\[f_i=f_j-size(i)+(n-size(i)) \]其中,\(j\)為\(i\)的父節點,\(size(i)\)表示以\(i\)為根的子樹大小,方程很直觀.
特別地,根節點的\(f\)是所有節點的深度之和(深度從0開始).
程式碼
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#define int long long
using namespace std;
int read() {
int re = 0;
char c = getchar();
bool negt = false;
while(c < '0' || c > '9')
negt |= (c == '-') , c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return negt ? -re : re;
}
const int N = 200010;
struct EDGE {
int to , nxt;
}ed[N * 2];
int head[N];
void addedge(int u , int v) {
static int cnt;
++cnt;
ed[cnt].to = v , ed[cnt].nxt = head[u] , head[u] = cnt;
}
int n;
int dep[N];
int siz[N];
int f[N];
void dfs(int x , int fa) {
siz[x] = 1;
dep[x] = dep[fa] + 1;
for(int i = head[x] ; i ; i = ed[i].nxt) {
if(ed[i].to != fa)
dfs(ed[i].to , x) , siz[x] += siz[ed[i].to];
}
}
void dfs2(int x , int fa) {
if(x != 1)
f[x] = f[fa] + n - siz[x] - siz[x];
for(int i = head[x] ; i ; i = ed[i].nxt) {
if(ed[i].to != fa)
dfs2(ed[i].to , x);
}
}
signed main() {
n = read();
for(int i = 1 ; i < n ; i++) {
int u = read() , v = read();
addedge(u , v) , addedge(v , u);
}
dep[0] = -1;
dfs(1 , 0);
for(int i = 1 ; i <= n ; i++)
f[1] += dep[i];
dfs2(1 , 0);
for(int i = 1 ; i <= n ; i++) {
printf("%lld\n" , f[i]);
}
return 0;
}
G - Isosceles Trapezium
題目
![image-20210928214139088]([ABC220]AtCoder Beginner Contest 220.assets/image-20210928214139088.png)
思路
四個點能構成等腰梯形,當且僅當兩點的中垂線與另外兩點的中垂線重合,且中點不重合.
然後大水題,STL亂搞.
程式碼
#include <bits/stdc++.h>
using namespace std;
int read() {
int re = 0;
char c = getchar();
bool negt = false;
while(c < '0' || c > '9')
negt |= (c == '-') , c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return negt ? -re : re;
}
const int N = 1010;
typedef long long ll;
typedef pair<pair<ll , ll> , ll> line;
typedef pair<ll , ll> point;
line makeline(ll A , ll B , ll C) {
return make_pair(make_pair(A , B) , C);
}
ll gcd(ll a , ll b) {
return b == 0 ? a : gcd(b , a % b);
}
int n;
ll x[N] , y[N];
ll w[N];
#define index index_
map <line , int> index;
map <point , ll> a[N * N];
int cnt;
int main() {
n = read();
for(int i = 1 ; i <= n ; i++)
x[i] = read() * 2 , y[i] = read() * 2 , w[i] = read();
for(int i = 1 ; i <= n ; i++)
for(int j = i + 1 ; j <= n ; j++) {
point mid;
mid.first = (x[i] + x[j]) / 2 , mid.second = (y[i] + y[j]) / 2;
ll A , B , C;//直線一般式方程,防止噁心的浮點數(精度問題很麻煩),也防止斜率不存在的尷尬情況.
ll delx = x[i] - x[j];
ll dely = y[i] - y[j];
ll sumw = w[i] + w[j];
if(delx == 0)
A = 0 , B = 1 , C = -(y[i] + y[j]) / 2;
else if(dely == 0)
A = 1 , B = 0 , C = -(x[i] + x[j]) / 2;
else {
A = delx , B = dely , C = -dely * mid.second - delx * mid.first;
ll g = (C == 0 ? gcd(A , B) : gcd(gcd(A , B) , C));//化簡,保證重合的直線寫出來的方程一致
A /= g , B /= g , C /= g;
if(A < 0)
A = -A , B = -B , C = -C;
}
line l = makeline(A , B , C);
// printf("%lld\t%lld\t%lld:\t%lld\n" , A , B , C , sumw);
int id;
if(index.find(l) == index.end())
index[l] = id = ++cnt;
else
id = index[l];
if(a[id].find(mid) == a[id].end())
a[id][mid] = sumw;
else
a[id][mid] = max(a[id][mid] , sumw);
}
ll ans = -1;
for(int i = 1 ; i <= cnt ; i++) {
ll max1 = -1 , max2 = -1;
for(auto j = a[i].begin() ; j != a[i].end() ; j++) {
ll val = j->second;
if(val > max1)
max2 = max1 , max1 = val;
else if(val > max2)
max2 = val;
}
if(max1 != -1 && max2 != -1 && ans < max1 + max2)
ans = max1 + max2;
}
cout << ans;
return 0;
}