Preface
最唐氏的一集,前中期被 A 卡得數次破防紅溫,後期經典不知道在幹嘛擺著擺著就結束了
可惜的是徐神最後 1h 寫的 B 因為兩個陣列搞反了一直沒過,賽後看了眼就過了,這下狠狠地掉 Rating 了
雞爪
丁真構造題,但有人連 WA 三發怎麼回事呢
首先不難想到最大化和 \(1\) 連邊的數量,首先可以以 \(1\) 為中心搞一個雞爪,剩下的雞爪都可以蹭一條和 \(1\) 的邊
手玩一下會發現此時邊數最多為 \(2+\lfloor\frac{n}{3}\rfloor+n\bmod 3\) 條,而剩下的邊數總是偶數
此時要在剩下的點間合理地選出若干個雞爪,可以考慮這樣兩條兩條的加邊:
(2,3),(2,4)
(2,5).(3,4)
(2,6),(3,5)
不難發現這樣顯然保證了字典序最小
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
typedef pair <int,int> pi;
int t,n;
int main()
{
for (scanf("%d",&t);t;--t)
{
RI i; scanf("%d",&n); int e1; vector <pi> ans;
if (n<=2) e1=n; else e1=2+n/3+n%3;
for (i=2;i<=e1+1;++i) ans.push_back(pi(1,i));
int rem=n-e1; if (rem>0)
{
ans.push_back(pi(2,3));
ans.push_back(pi(2,4));
rem-=2;
}
for (i=5;i<=e1+1&&rem>0;++i)
{
ans.push_back(pi(2,i));
ans.push_back(pi(3,i-1));
rem-=2;
}
sort(ans.begin(),ans.end());
for (auto [x,y]:ans) printf("%d %d\n",x,y);
}
return 0;
}
夢中的地牢戰鬥
很裸的大力狀壓題,不難發現用三元組 \((x,y,mask)\) 表示當前角色位於 \((x,y)\),剩下沒打死的怪物狀壓狀態為 \(mask\) 的最大收益
預處理轉移後可以得到一個有向圖,但由於可能會成環因此需要用 SPFA 求最長路,細節比較多很容易寫掛
#include <bits/stdc++.h>
std::tuple<int, int, int> decomp(int status) {
return { status & 31, status >> 5 & 31, status >> 10 };
}
int comp(int x, int y, int e) {
return (x) | (y << 5) | (e << 10);
}
int dis[1 << 20], vis[1 << 20], dmg[1 << 20];
int ex[10], ey[10], ea[10], eb[10], ec[10];
int U[30][30][10], D[30][30][10], L[30][30][10], R[30][30][10], eas[1 << 10];
int n, m, k, d;
inline int L1(int x1, int y1, int x2, int y2) {
return std::abs(x1 - x2) + std::abs(y1 - y2);
}
int work() {
std::cin >> n >> m >> k >> d;
int emap[30][30]; memset(emap, 0, sizeof(emap));
for(int i = 0; i < k; ++i)
std::cin >> ex[i] >> ey[i],
emap[--ex[i]][--ey[i]] |= 1 << i,
std::cin >> ea[i] >> eb[i] >> ec[i];
for(int i = 0; i < n; ++i) for(int j = 0; j < m; ++j) for(int k = 0; k <= d; ++k)
U[i][j][k] = D[i][j][k] = L[i][j][k] = R[i][j][k] = emap[i][j];
// U[i][j][1] = D[i][j][1] = L[i][j][1] = R[i][j][1] =*/ 0;
for(int z = 1; z <= d; ++z) for(int i = 0; i < n; ++i) for(int j = 0; j < m; ++j) {
if(i > 0) U[i][j][z] = U[i - 1][j][z - 1] | emap[i][j];
if(i < n - 1) D[i][j][z] = D[i + 1][j][z - 1] | emap[i][j];
if(j > 0) L[i][j][z] = L[i][j - 1][z - 1] | emap[i][j];
if(j < m - 1) R[i][j][z] = R[i][j + 1][z - 1] | emap[i][j];
}
memset(eas, 0, sizeof(eas));
for(int i = 0; i < (1 << k); ++i) {
eas[i] = 0;
for(int j = 0; j < k; ++j) if(i >> j & 1) eas[i] += ea[j];
}
memset(vis, 0, sizeof(vis));
memset(dis, 0x80, sizeof(dis));
for(int x = 0; x < n; ++x) for(int y = 0; y < m; ++y) for(int e = 0; e < (1 << k); ++e) {
int c = comp(x, y, e); dmg[c] = 0;
for(int z = 0; z < k; ++z) if((e >> z & 1) && L1(x, y, ex[z], ey[z]) <= ec[z]) dmg[c] += eb[z];
// std::cerr << "dmg[" << x << "][" << y << "][" << e << "] = " << dmg[c] << std::endl;
}
int sx, sy; std::cin >> sx >> sy; sx--, sy--;
int ans = 0;
int start = comp(sx, sy, (1 << k) - 1);
dis[start] = 0; vis[start] = 1;
std::queue<int> q; q.push(start);
while(!q.empty()) {
int id = q.front(); q.pop();
if(dis[id] > ans) ans = dis[id];
vis[id] = false;
auto [x, y, e] = decomp(id);
assert(!(e & emap[x][y]));
// std::cerr << "dis[" << x << "][" << y << "][" << e << "] = " << dis[id] << char(10);
auto update = [&](int nid, int r) {
if(dis[id] + r > dis[nid]) {
dis[nid] = dis[id] + r;
if(!vis[nid]) {
vis[nid] = true;
q.push(nid);
}
}
};
for(int i = 1; i <= d; ++i) {
int nx, ny, ne, nid, r;
nx = x - i, ny = y, ne = e ^ (e & U[x][y][i]);
if(nx >= 0 && !(emap[nx][ny] & e)) nid = comp(nx, ny, ne), r = eas[e & U[x][y][i]] - dmg[nid], update(nid, r);
nx = x + i, ny = y, ne = e ^ (e & D[x][y][i]);
if(nx < n && !(emap[nx][ny] & e)) nid = comp(nx, ny, ne), r = eas[e & D[x][y][i]] - dmg[nid], update(nid, r);
ny = y - i, nx = x, ne = e ^ (e & L[x][y][i]);
if(ny >= 0 && !(emap[nx][ny] & e)) nid = comp(nx, ny, ne), r = eas[e & L[x][y][i]] - dmg[nid], update(nid, r);
ny = y + i, nx = x, ne = e ^ (e & R[x][y][i]);
if(ny < m && !(emap[nx][ny] & e)) nid = comp(nx, ny, ne), r = eas[e & R[x][y][i]] - dmg[nid], update(nid, r);
}
}
return ans;
}
int main() {
std::ios::sync_with_stdio(false);
int T; std::cin >> T; while(T--) {
std::cout << work() << std::endl;
}
return 0;
}
絕對不模擬的簡單魔方
魔方大師祁神發現這題根本不用 Care 什麼旋轉,角塊的排布就是符合一定拓撲性質的,然後秒了此題
#include<bits/stdc++.h>
using namespace std;
const int corner[8][3][2] = {
{{3, 4}, {4, 3}, {4, 4}},
{{3, 6}, {4, 6}, {4, 7}},
{{1, 6}, {4, 9}, {4, 10}},
{{1, 4}, {4, 12}, {4, 1}},
{{7, 4}, {6, 4}, {6, 3}},
{{7, 6}, {6, 7}, {6, 6}},
{{9, 6}, {6, 10}, {6, 9}},
{{9, 4}, {6, 1}, {6, 12}},
};
int t;
string cube[10];
int gp(int c, int p){
auto [x, y] = corner[c][p];
return cube[x][y]-'0';
}
int getnum(int c){
return gp(c, 0)*100 + gp(c, 1)*10 + gp(c, 2);
}
void rorate(int &x){
int tmp = x%10;
x /= 10;
x += tmp*100;
}
void solve(){
set<int> st({123, 134, 145, 152, 632, 643, 654, 625});
for (int i=1; i<=9; ++i){
cin >> cube[i];
cube[i] = '$' + cube[i];
}
bool ok=true;
int ans=0;
for (int c=0; c<8; ++c){
int x = getnum(c);
while ((x/100 != 1) && (x/100 != 6)) rorate(x);
if (!st.count(x)){ok=false; ans=x; break;}
}
if (ok) cout << "No problem\n";
else{
vector<int> vec({ans/100, (ans/10)%10, ans%10});
sort(vec.begin(), vec.end());
cout << vec[0] << ' ' << vec[1] << ' ' << vec[2] << '\n';
}
}
signed main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> t; while (t--) solve();
return 0;
}
a*b problem
不可做題,棄療
小塔的養成遊戲之夢
賽時沒幾個隊過,棄療
傳奇勇士小凱
根據題意就是選一條從根到葉子的路徑,然後簡單推一下期望的式子會發現每個點的貢獻就是 \(\frac{15}{p_i}\)
手寫一個分數類,不難發現只有加法的情況分母不會超過 \(\operatorname{LCM}(1,2,\dots,15)\),可以直接求解無需高精度
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
struct frac
{
int x,y;
inline frac(CI X=0,CI Y=1)
{
int g=__gcd(X,Y); x=X/g; y=Y/g;
}
friend inline bool operator < (const frac& A,const frac& B)
{
return A.x*B.y<A.y*B.x;
}
friend inline frac operator + (const frac& A,const frac& B)
{
int X=A.x*B.y+A.y*B.x,Y=A.y*B.y,g=__gcd(X,Y);
return frac(X/g,Y/g);
}
}f[N]; int t,n,x,y,a[N]; vector <int> v[N];
inline void DFS(CI now=1,CI fa=0)
{
f[now]=frac(15,a[now]); frac tmp;
for (auto to:v[now]) if (to!=fa)
{
DFS(to,now);
if (tmp<f[to]) tmp=f[to];
}
f[now]=f[now]+tmp;
}
signed main()
{
for (scanf("%lld",&t);t;--t)
{
RI i; for (scanf("%lld",&n),i=1;i<=n;++i) v[i].clear();
for (i=1;i<n;++i) scanf("%lld%lld",&x,&y),v[x].push_back(y),v[y].push_back(x);
for (i=1;i<=n;++i) scanf("%lld",&a[i]);
DFS(); printf("%lld/%lld\n",f[1].x,f[1].y);
}
return 0;
}
URL劃分
簽到,我題意都不知道
#include <bits/stdc++.h>
int main() {
int T; std::cin >> T; while(T--) {
std::string s; std::cin >> s;
int i = 0;
while(s[i] != ':') i++;
std::cout << s.substr(0, i) << char(10);
i += 3;
int pre = i;
while(s[i] != '/') i++;
std::cout << s.substr(pre, i - pre) << char(10);
for(;;) {
pre = i + 1;
if(pre >= s.size()) break;
bool flag = false;
i = pre;
while(s[i] != '/') {
if(s[i] == '=') flag = true;
i += 1;
}
if(flag) std::cout << s.substr(pre, i - pre) << char(10);
}
}
return 0;
}
成長,生命,幸福
很有趣的一個題
首先要觀察到一個重要的性質,即一棵樹成長一次後每個點的度數一定是 \(1/2/3\)
度數為 \(1\) 的一定是葉子不用管,度數為 \(2\) 的每次會分裂出兩個度數為 \(2\) 的,度數為 \(3\) 的每次會分裂出一個度數為 \(3\) 的和兩個度數為 \(2\) 的
考慮用一個二元組 \((x,y)\) 表示一條有 \(x\) 個度數為 \(2\) 的點,\(y\) 個度數為 \(3\) 的點的路徑,則進行 \(m-1\) 次成長後其貢獻為 \(2^{m-1}\times x+(2^m-1)\times y\)
不難發現在模意義下不能直接比較兩個二元組的大小,但我們發現這兩個係數在 \(m\) 增大時會無限趨近於 \(\frac{1}{2}\)
因此當 \(m\le 20\) 時,可以直接用對應的係數帶進去比較;否則直接把係數定為 \(\frac{1}{2}\) 算出最大的 \((x,y)\) 再計算答案即可
#include<cstdio>
#include<iostream>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
typedef long long LL;
const int N=100005,mod=1e9+7;
int t,n,m,x,y,A,B; vector <int> v[N];
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
struct ifo
{
int x,y;
inline ifo(CI X=0,CI Y=0)
{
x=X; y=Y;
}
inline LL val(void)
{
return 1LL*A*x+1LL*B*y;
}
friend inline ifo operator + (const ifo& A,const ifo& B)
{
return ifo(A.x+B.x,A.y+B.y);
}
}ans,f[N],mx[N],smx[N];
inline void DFS(CI now=1,CI fa=0)
{
if (v[now].size()>=2) f[now]=ifo(2,(int)v[now].size()-2); else f[now]=ifo();
mx[now]=smx[now]=ifo();
for (auto to:v[now]) if (to!=fa)
{
DFS(to,now);
if (f[to].val()>mx[now].val()) smx[now]=mx[now],mx[now]=f[to];
else if (f[to].val()>smx[now].val()) smx[now]=f[to];
}
if (f[now].val()+mx[now].val()+smx[now].val()>ans.val())
ans=f[now]+mx[now]+smx[now]; f[now]=f[now]+mx[now];
}
int main()
{
for (scanf("%d",&t);t;--t)
{
RI i; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) v[i].clear();
for (i=1;i<n;++i) scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
if (m<=20) A=1<<m-1,B=(1<<m)-1; else A=1,B=2;
ans=ifo(); DFS(); A=quick_pow(2,m-1); B=(quick_pow(2,m)-1+mod)%mod;
printf("%d\n",(ans.val()%mod+2)%mod);
}
return 0;
}
強攻計策
不可做題,棄療
女神的睿智
純純的簽到
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int t; char s[10];
int main()
{
for (scanf("%d",&t);t;--t)
{
scanf("%s",s+1);
if (s[1]==s[5]) printf("%c\n",s[1]); else
{
int c[2]={0,0};
for (RI i=1;i<=8;++i)
{
if (s[i]==s[1]) ++c[0];
if (s[i]==s[5]) ++c[1];
}
if (c[0]==c[1]) puts("N");
else printf("%c\n",c[0]>c[1]?s[1]:s[5]);
}
}
return 0;
}
在 A 裡面找有 C 的 B
題意都不知道的字串,被徐神一眼秒了,好像是個 AC 自動機
#include <bits/stdc++.h>
struct node_t {
int go[26];
int fail, tag;
node_t() { memset(this, 0, sizeof(node_t)); }
};
auto build_acam(
const std::vector<std::string> &s
) -> std::tuple<
std::vector<node_t>,
std::vector<int>,
std::vector<int>
> {
std::vector<node_t> res = { node_t() };
std::vector<int> pos = {};
for(auto s: s) {
int cur = 0;
for(auto c: s) {
int u = c - 'a';
if(!res[cur].go[u]) {
res[cur].go[u] = res.size();
res.push_back(node_t());
}
cur = res[cur].go[u];
}
pos.push_back(cur);
}
std::vector<int> q; int ql = 0;
for(int i = 0; i < 26; ++i) if(res[0].go[i])
q.push_back(res[0].go[i]);
while(ql < q.size()) {
int hd = q[ql++];
for(int i = 0; i < 26; ++i)
if(res[hd].go[i])
res[res[hd].go[i]].fail = res[res[hd].fail].go[i],
q.push_back(res[hd].go[i]);
else
res[hd].go[i] = res[res[hd].fail].go[i];
}
return { res, pos, q };
}
std::vector<int> work() {
int n; std::cin >> n;
std::string a, c;
std::vector<std::string> b1(n);
std::vector<std::string> b2(n);
std::cin >> a >> c;
for(int i = 0; i < n; ++i)
std::cin >> b1[i] >> b2[i];
std::vector<bool> is_ans(n, true);
{
auto [acam, pos, q] = build_acam(b1);
std::reverse(q.begin(), q.end());
for(int i = 0, cur = 0; i < a.size(); ++i) {
int u = a[i] - 'a';
cur = acam[cur].go[u];
acam[cur].tag = 1;
}
for(auto i: q) acam[acam[i].fail].tag |= acam[i].tag;
for(int i = 0; i < n; ++i) is_ans[i] = is_ans[i] && acam[pos[i]].tag;
}
{
auto [acam, pos, q] = build_acam({c});
for(auto pos: pos) acam[pos].tag = 1;
for(auto q: q) acam[q].tag |= acam[acam[q].fail].tag;
for(int i = 0; i < n; ++i) {
int cur = 0;
bool flag = false;
for(auto c: b2[i]) {
cur = acam[cur].go[c - 'a'];
flag = flag || acam[cur].tag;
}
is_ans[i] = is_ans[i] && flag;
}
}
std::vector<int> ans;
for(int i = 0; i < n; ++i) if(is_ans[i]) ans.push_back(i);
return ans;
}
int main() {
std::ios::sync_with_stdio(false);
int T; std::cin >> T; while(T--) {
std::vector<int> ans = work();
if(ans.empty()) std::cout << char(10);
for(int i = 0; i < ans.size(); ++i)
std::cout << ans[i] + 1 << char(i == ans.size() - 1 ? 10 : 32);
}
return 0;
}
圖計算
唉經典原題大戰,但這次是沒做過的原題
考慮如何判斷兩個點 \(x,y\) 在 \(d+1\) 張圖內都連通,我們可以給每個點求出一個長為 \(d+1\) 的向量,其中第 \(i\) 個元素代表該點在第 \(i\) 個圖內的祖先,此時 \(x,y\) 連通當且僅當它們對應的向量相同
用 Hash 維護每個點的向量,每次修改時在對應的圖中啟發式合併一下,將更新父親節點的點對應的 Hash 值一併修改即可
總複雜度 \(O(k\log^2 n+m\times d)\)
#include<cstdio>
#include<iostream>
#include<random>
#include<map>
#define RI register int
#define CI const int&
using namespace std;
typedef unsigned long long u64;
const int N=50005;
mt19937 rng;
uniform_int_distribution <u64> dist(0,1ull<<63);
int t,n,m,d,k,x[N<<1],y[N<<1]; u64 hsh[N];
long long ans; map <u64,int> rst;
inline void upt(const u64& x,CI y)
{
ans-=1LL*rst[x]*(rst[x]-1)/2LL;
rst[x]+=y;
ans+=1LL*rst[x]*(rst[x]-1)/2LL;
}
struct DSU
{
int fa[N]; vector <int> v[N]; u64 seed;
inline void init(CI n)
{
seed=dist(rng);
for (RI i=1;i<=n;++i) fa[i]=i,v[i]={i},hsh[i]+=u64(i)*seed;
}
inline void merge(int x,int y)
{
x=fa[x]; y=fa[y];
if (x==y) return;
if (v[x].size()<v[y].size()) swap(x,y);
for (auto u:v[y])
{
fa[u]=x; upt(hsh[u],-1);
hsh[u]-=u64(y)*seed;
hsh[u]+=u64(x)*seed;
upt(hsh[u],1); v[x].push_back(u);
}
v[y].clear();
}
}G[105];
int main()
{
//freopen("1012.in","r",stdin); freopen("my.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i,j; scanf("%d%d%d%d",&n,&m,&d,&k);
for (i=1;i<=n;++i) hsh[i]=0;
for (i=1;i<=m;++i) scanf("%d%d",&x[i],&y[i]);
for (i=1;i<=d+1;++i) G[i].init(n);
for (ans=0,rst.clear(),i=1;i<=n;++i) upt(hsh[i],1);
for (i=1;i<=d+1;++i)
for (j=1;j<=m;++j) G[i].merge(x[j],y[j]);
for (i=1;i<=k;++i)
{
int u,v,w; scanf("%d%d%d",&u,&v,&w);
G[w].merge(u,v);
printf("%lld\n",ans);
}
}
return 0;
}
Postscript
感覺現在一到後期就開始白蘭摸魚,一點作用都沒有,怎麼回事呢