每次vp都自閉,已成習慣,時間還是分配的不合理,debug時做太多無用功。
一鍵做題
A.交小西的禮物
輸出 a + b + 2c + 3d 即可
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int mod = 998244353;
const int N = 1e5 + 7;
void solve()
{
ll a,b,c,d;
cin >> a>> b >> c>> d;
cout << a + b + c*2 + d *3 << '\n';
}
C.榕樹之心
直接按題目要求計算
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
const int N = 1e5 + 7;
const int mod = 998244353;
void solve()
{
double x, y;
cin >> x >> y;
double x0 = 0.5 * x + 0.5 * y;
double y0 = sqrt(3) * 0.5 * x - sqrt(3) * 0.5 * y;
cout << fixed << setprecision(7) << x0 << " " << y0 << '\n';
}
B.轉呀轉
直接用半徑和圓心角求弦長即可,注意sin中引數是弧度制
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int mod = 998244353;
const int N = 1e5 + 7;
const double pi = acos(-1.0);
void solve()
{
double x, y;
cin >> x >> y;
double r = (x * x) + y * y;
r = sqrt(r);
double t;
cin >> t;
double v;
cin >> v;
v = 1.0 / v;
double p = t / v;
p = p - (int)p;
double q = 1.0 - p;
if (p > q)
swap(p, q);
double ans = p * pi;
ans = sin(ans) * 2.0 * r;
cout << fixed << setprecision(8) << ans << '\n';
}
F.Everyone’s ALL IN
預處理計算即可
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int mod = 998244353;
const int N = 1e6 + 7;
const double pi = acos(-1.0);
vector<int>g[N];
void solve()
{
int n,m;
cin>>n>>m;
map<ll,ll>mp;
for(int i=1;i<=n;i++)
{
ll x;
cin>>x;
g[x].push_back(i);
mp[x]+=i;
}
while(m--)
{
int x,y;
cin>>x>>y;
cout<<1ll*mp[x]*mp[y]<<endl;
}
}
D.瑟莉姆的宴會
直接構造菊花圖即可,對於每個點的正負貢獻都計算一下,如果該點非負,即以該點為根構造菊花圖
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int mod = 998244353;
const int N = 1e6 + 7;
const double pi = acos(-1.0);
void solve()
{
int n,m;
cin >> n >> m;
vector<int>a(n + 1);
for(int i=0;i<m;i++){
int u,v;
cin >> u >> v;
a[u]++,a[v]--;
}
int rt = 0;
for(int i=1;i<=n;i++){
if(a[i]>=0){
rt = i;
}
}
for(int i=1;i<=n;i++)
{
if(i==rt){
cout << 0 << ' ';
}else {
cout << rt << " " ;
}
}
}
O.篩法
打表猜答案為\(n^{2}\),證明不會(
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int mod = 998244353;
const int N = 1e6 + 7;
const double pi = acos(-1.0);
void solve()
{
ll n;
cin >> n;
cout << n * n << endl;
}
M.生命遊戲
就直接暴力模擬即可,使用bfs模擬,若度為k進佇列執行後刪除,每個點最多進隊一次
最後再使用遍歷一遍圖得到答案
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
const int N = 1e6 + 7;
const int mod = 998244353;
const double eps = 1e-9;
vector<int> s[N];
void solve()
{
int n, k;
cin >> n >> k;
vector<int> du(n + 1);
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
s[u].push_back(v);
s[v].push_back(u);
du[u]++, du[v]++;
}
queue<int> q;
for (int i = 1; i <= n; i++)
{
if (du[i] == k)
{
q.push(i);
du[i] = -1;
}
}
vector<int> vis(n + 1);
int ans = 0;
while (q.size())
{
set<int> st;
while (q.size())
{
int u = q.front();
q.pop();
vis[u] = 1;
for (auto v : s[u])
{
if (du[v] == -1)
continue;
du[v]--;
if (du[v] == k)
{
st.insert(v);
}
else
{
if (st.count(v))
st.erase(v);
}
}
}
for (auto v : st)
{
q.push(v);
du[v] = -1;
}
}
auto bfs = [&](int u)
{
queue<int> qp;
qp.push(u);
vis[u] = 1;
while (qp.size())
{
int now = qp.front();
qp.pop();
for (auto v : s[now])
{
if (!vis[v])
{
qp.push(v);
vis[v] = 1;
}
}
}
};
for (int i = 1; i <= n; i++)
{
if (!vis[i])
{
bfs(i);
ans++;
}
}
cout << ans << '\n';
}
K.崩壞:星穹鐵道
沒玩崩鐵導致的,讀假題演了半小時(
一看資料範圍加上求方案數自然想到矩陣快速冪
考慮如何構造矩陣
發現對於每一時刻,只需用當前所剩技能點進行轉移即可,矩陣大小6*6
透過dfs構造矩陣,列舉初始技能點i,搜尋經過四次(一輪)攻擊後所到達的狀態j有多少方案
得到轉移矩陣後進行n/4次轉移即可
最後要與初始狀態相乘,也就是有k個技能點
由於n不一定是4的倍數,所以要再用dfs轉移完餘數的貢獻
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
const int N = 1e6 + 7;
const int mod = 998244353;
const double eps = 1e-9;
#define int long long
struct Mat
{
int v[130][130] = {0};
int x, y;
Mat() {}
Mat(int _x, int _y) { x = _x, y = _y; }
// 清空
void zero()
{
memset(v, 0, sizeof(v));
x = y = 0;
}
// 單位矩陣化
void ones()
{
memset(v, 0, sizeof(v));
for (int i = 0; i <= x; i++)
{
v[i][i] = 1;
}
}
// 乘法
void Mul(Mat a, Mat b)
{
zero();
x = a.x, y = b.y;
int c = a.y;
for (int i = 0; i <= x; ++i)
{
for (int j = 0; j <= y; ++j)
{
for (int k = 0; k <= c; ++k)
{
v[i][j] += (long long)a.v[i][k] * b.v[k][j] % mod;
v[i][j] %= mod;
}
}
}
return;
}
// 列印
void show()
{
for (int i = 0; i <= x; i++)
{
for (int j = 0; j <= y; j++)
{
cout << v[i][j] << " ";
}
cout << '\n';
}
}
};
Mat operator*(const Mat &x, const Mat &y)
{
Mat res;
res.Mul(x, y);
return res;
}
Mat ksm(Mat a, long long b)
{
Mat x = a;
x.ones();
while (b)
{
if (b & 1)
x = x * a;
b >>= 1;
a = a * a;
}
return x;
}
int ps[5];
Mat a(5, 5);
void dfs(int u, int i, int r, int lim)
{
if (i == lim)
{
a.v[r][u]++;
return;
}
if (ps[i] == 1)
{
u++;
if (u > 5)
u = 5;
dfs(u, i + 1, r, lim);
}
if (ps[i] == 2)
{
if (u == 0)
u++;
else
u--;
dfs(u, i + 1, r, lim);
}
if (ps[i] == 3)
{
int v = u;
v++;
if (v > 5)
v = 5;
dfs(v, i + 1, r, lim);
if (u != 0)
{
u--;
dfs(u, i + 1, r, lim);
}
}
}
void solve()
{
ll n, k;
cin >> n >> k;
for (int os = 1; os <= 4; os++)
{
cin >> ps[os];
}
for (int i = 0; i <= 5; i++)
{
dfs(i, 1, i, 5);
}
Mat p(0, 5);
ll nm = n % 4;
p.v[0][k] = 1;
Mat res = p * ksm(a, n / 4);
ll ans = 0;
a.zero();
a = Mat(5, 5);
for (int i = 0; i <= 5; i++)
{
dfs(i, 1, i, n % 4 + 1);
}
for (int i = 0; i <= 5; i++)
{
ll sb = 0;
for (int j = 0; j <= 5; j++)
{
sb += res.v[0][i] * a.v[i][j] % mod;
sb %= mod;
}
ans += sb;
ans %= mod;
}
cout << ans << '\n';
}
E.雪中樓
沒想到題解期望難度這麼高
看完題的第一想法就是直接模擬,考慮如何快速實現,我這裡使用了一個雙端連結串列
l記錄左邊編號,r記錄右邊編號,直接插入即可
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int mod = 998244353;
const int N = 2e5 + 7;
const double pi = acos(-1.0);
int l[N],r[N];
void solve()
{
int n;
cin >> n;
for(int i=1;i<=n;i++){
int x;
cin >> x;
if(x == 0){
int p = r[0];
l[r[0]] = i;
r[0] = i;
l[i] = 0;
r[i] = p;
}else {
int p = r[x];
l[r[x]] = i;
r[x] = i;
l[i] = x;
r[i] = p;
}
}
int x = r[0];
while(x!=0){
cout << x << ' ';
x = r[x];
}
}
I.命令列
從寫完E之後就一直在想這道題,感覺題目描述有些不清晰一直wa最後一個點,結束後看別人程式碼才發現坑點,如果串為空那麼補全是集合T為所有指令串(空串是所有串的字首?)
要求字首考慮使用字典樹,用cnt記錄每個字首有多少串經過,並記錄有每個串的終點out
我們執行操作的時候記錄當前指標p,使用p在字典樹上移動,如果該節點不存在,此時p = -1,同時用一個k記錄-1的個數,一個lst記錄最後在字典樹上的節點編號。當p=-1時,增加操作使k增加,退格操作同理,執行操作無解,補全操作也是沒有行動。
考慮不是-1的情況,即當前輸入串還在字典樹上,如果是加入操作就直接執行,如果是刪除操作,我們可以對於字典樹上每個節點p都記錄一個父親節點,那麼刪除操作就是在字典樹上跳父親。
對於執行操作,如果當前p是終點out,那麼輸出out[p],否則無解。
對於補全操作,一個最樸素的想法是在樹上一直跳,直到有分叉為止,但是會被一個很長的串卡掉
考慮最佳化,我們在字典樹預處理時再記錄一下每個節點有哪些串經過in(可能有多個,但是不影響答案),再記錄對於每個串的每個字母對應的節點mp,這樣我們可以在補全操作時對於當前串二分,如果二分到的位置的節點的cnt小於最初的cnt就不行,這樣就得到了補全後的節點p,此時即可透過此題
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
const int N = 5e6 + 7;
const int mod = 998244353;
// const int N = 1000050;
int trie[N][26]; // 所構造的查詢樹,其對應的值是子節點的編號,【它的編號】【子節點的字母】
// 等於0表示沒有該字母;
int cnt[N]; // N是一個單詞終點的編號,表示有無該單詞(0表示沒有)
int id; // 結點計數器,初始值為0
// 每次插入和查詢複雜度都是O(n)
int fa[N];
int in[N];
int out[N];
void solve()
{
int n, m;
cin >> n >> m;
vector<string> str(n + 1);
vector<vector<int>> mp(n + 1);
// in[0]=1;
for (int i = 1; i <= n; i++)
{
cin >> str[i];
int p = 0;
mp[i].resize(str[i].size());
in[p] = i;
cnt[p]++;//記錄0節點
for (int j = 0; j < str[i].size(); j++)
{
int x = str[i][j] - 'a';
if (trie[p][x] == 0)
trie[p][x] = ++id;
int suv = p;
p = trie[p][x];
fa[p] = suv;
cnt[p]++;
mp[i][j] = p;//每個字元對應節點
in[p] = i;//每個節點被哪個經過
}
out[p] = i;//是否是一個串的終點
}
string TS;
cin >> TS;
int p = 0, lst = 0, k = 0;
for (auto it : TS)
{
if (it == 'T')
{
if (p == -1)
{
continue;
}
int ls = cnt[p];//當前節點經過串個數
int i = in[p];//經過當前節點的一個串的編號
int l = 0, r = str[i].size() - 1;
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (cnt[mp[i][mid]] >= ls)
{
l = mid;
}
else
{
r = mid - 1;
}
}
if(cnt[mp[i][l]]<ls){//檢查最終結果是否合法(主要針對空串情況)
p = 0;
}else
p = mp[i][l];//跳到補全後的位置
}
else if (it == 'B')
{
if (p == 0)
continue;
if (p == -1)
{
k--;
if (k == 0)//沒有-1,當前輸入又回到樹上
{
p = lst;
}
}
else
{
p = fa[p];//跳父親
}
}
else if (it == 'E')
{
if (p != -1 && out[p])
{
cout << out[p] << ' ';
}
else
{
cout << -1 << ' ';
}
p = 0;//清空輸入
lst = 0;
k = 0;
}
else
{
if (p == -1)
{
k++;
continue;
}
int x = it - 'a';
int suv = p;
p = trie[p][x];
if (!p)//開始沒有對應節點
{
lst = suv;
k++;
p = -1;
}
}
}
}
N.聖誕樹
十分簡單的啟發式合併
一個貪心的想法是隻要當前子樹有k個不同,就對答案貢獻
令1為根
對每個節點開一個set存子樹中節點顏色,如果子樹已經貢獻答案則不進入set
合併時候從小到大合併,確保複雜度
整體複雜度\(O(nlog^{2}n)\)
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f
using namespace std;
using ll = long long;
const int N = 5e5 + 7;
const int mod = 998244353;
vector<int> s[N];
set<int> st[N];
void solve()
{
int n, k;
cin >> n >> k;
vector<int> col(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> col[i];
}
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
s[u].push_back(v);
s[v].push_back(u);
}
int ans = 0;
auto dfs = [&](auto &dfs, int u, int fa) -> void
{
st[u].insert(col[u]);
for (auto v : s[u])
{
if (v == fa)
continue;
dfs(dfs, v, u);
if(st[v].size()>st[u].size()){
swap(st[u],st[v]);//啟發式合併
}
for(auto x:st[v]){
st[u].insert(x);
}
}
if (st[u].size() >= k)
{
ans++;
st[u].clear();
}
};
dfs(dfs,1,0);
cout << ans << '\n';
}