Preface
去年的香港站,本来两个礼拜没摸键盘了,但意外打的很顺,最后出了 7 个题感觉都能干进捧杯区了,只能感慨摆烂使人进步啊
B. Defeat the Enemies
考虑 DP,令 \(f_{i,j}\) 表示一共造成了 \(i\) 点伤害,且被护甲稀释的伤害最多为 \(j\) 所需的最小代价,方案数顺便一起维护
由于被护甲稀释的伤害 \(<k\),因此第二维是 \(O(k)\) 的,DP 转移时只需要枚举下一击的伤害量,总复杂度 \(O(mk^2)\)
实现时有部分边界需要特别注意
#include <bits/stdc++.h>#define int int64_tconstexpr int mod = 998244353;void chmax(int &a, int b) {if(b > a) a = b;
}void chmin(int &a, int b) {if(b < a) a = b;
}void add(int &a, int b) {if((a += b) >= mod) a -= mod;
}void work() {int n, m; std::cin >> n >> m;std::vector<int> a(n), b(n);for(auto &a: a) std::cin >> a;for(auto &b: b) std::cin >> b;int k; std::cin >> k;std::vector<int> c(k + 1);for(int i = 1; i <= k; ++i) std::cin >> c[i];std::vector<std::array<int, 101>> prep(m + 1);for(int i = 0; i <= m; ++i) for(int j = 0; j <= 100; ++j)prep[i][j] = 0;for(int i = 0; i < n; ++i)for(int j = 1; j <= k; ++j)chmax(prep[b[i] - 1][j], a[i] + b[i] + j - 1);for(int j = 1; j < k; ++j)for(int i = m; i >= 1; --i)chmax(prep[i - 1][j + 1], prep[i][j]);int hkr = 0;for(int i = 0; i < n; ++i) chmax(hkr, a[i] + b[i]);const int U = 2 * m + 2 * k + 100;std::vector<std::array<int, 100>>dp1(U + 1),dp2(U + 1);for(int i = 0; i <= U; ++i) for(int j = 0; j < 100; ++j)dp1[i][j] = 0x3fffffffffffffffLL, dp2[i][j] = 0;dp1[0][0] = 0;dp2[0][0] = 1;int ans = 0x3fffffffffffffffLL, ans_count = 0;for(int i = 0; i <= U; ++i) for(int j = 0; j < k; ++j) {if(i >= hkr + j) {if(dp1[i][j] < ans) ans = dp1[i][j], ans_count = dp2[i][j]; elseif(dp1[i][j] == ans) add(ans_count, dp2[i][j]);}for(int x = 1; x <= k && i + x <= U; ++x) {int ncost = dp1[i][j] + c[x];int ni = i + x;int nj = j;if(i <= m) chmax(nj, prep[i][x] - hkr);if(ncost < dp1[ni][nj]) {dp1[ni][nj] = ncost;dp2[ni][nj] = dp2[i][j];} elseif(ncost == dp1[ni][nj]) {add(dp2[ni][nj], dp2[i][j]);}}}std::cout << ans << ' ' << ans_count << char(10);return ;
}int32_t main() {std::ios::sync_with_stdio(false);int T; std::cin >> T; while(T--) work();return 0;
}
C. The Story of Emperor Bie
签到,首先不难发现答案只可能是最大值所在的位置
手玩一下会发现其实从这些位置的任意一个开始推都是等价的,而题目又保证一定有解,因此输出这些位置即可
#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=500005;
int t,n,a[N];
int main()
{for (scanf("%d",&t);t;--t){scanf("%d",&n);for (RI i=1;i<=n;++i) scanf("%d",&a[i]);int mx=*max_element(a+1,a+n+1);for (RI i=1;i<=n;++i) if (a[i]==mx) printf("%d ",i);putchar('\n');}return 0;
}
E. Concave Hull
挺难的一个几何题,祁神赛时+赛后调了挺久才过的,我连题目都没看就不做评论了
#include<bits/stdc++.h>
using namespace std;
#define int long longconst int N = 2005;
const int MOD = (int)1e9+7;void inc(int &x, int a) {if ((x+=a)>=MOD) x-=MOD;}
void dec(int &x, int a) {if ((x-=a)<0) x+=MOD;}struct Pt {int x, y;Pt operator-(const Pt &b) const {return Pt{x-b.x, y-b.y};}Pt operator+(const Pt &b) const {return Pt{x+b.x, y+b.y};}bool operator<(const Pt &b) const {return x!=b.x ? x<b.x : y<b.y;}int crs(const Pt &b) const {return x*b.y-y*b.x;}int quad() const {if (x>0 && y>=0) {return 1;} if (x<=0 && y>0) {return 2;}if (x<0 && y<=0) {return 3;} if (x>=0 && y<0) {return 4;}return 0;}
};struct arcPt {Pt v;int qd, id;bool operator< (const arcPt b) const {if (qd != b.qd) return qd < b.qd;else return v.crs(b.v) > 0;}
};#define ft first
#define sd secondint n, ans, areaConvh;
int stk[N]; int tp=-1;
Pt pt[N];
bool onConvh[N];
vector<arcPt> arc[N];vector<int> makeConvh(vector<int> &vec) {sort(vec.begin(), vec.end(), [&](int a, int b) {return pt[a] < pt[b];});int sz = vec.size();vector<int> res(sz+5);int top = -1;res[++top] = vec[0];for (int i=1; i<sz; ++i) {while (top > 0 && (pt[res[top]]-pt[res[top-1]]).crs(pt[vec[i]]-pt[res[top]]) <= 0) --top;res[++top] = vec[i];}int mx = top;for (int i=sz-2; i>=0; --i) {while (top > mx && (pt[res[top]]-pt[res[top-1]]).crs(pt[vec[i]]-pt[res[top]]) <= 0) --top;res[++top] = vec[i];}res.erase(res.begin()+top, res.end());return res;
}int getConvhArea(vector<int> convh) {int res = 0;int sz = convh.size();for (int i=0; i<sz; ++i) {res += (pt[convh[i]].crs(pt[convh[(i+1)%sz]])) % MOD;}return res;
}int cross(int p, int a, int b) {return abs((pt[a]-pt[p]).crs(pt[b]-pt[p]))%MOD ;}// int getPlace(int center, int after, int x, int sel) {
// Pt v = pt[x] - pt[center];
// int qd = v.quad();
// int res1 = lower_bound(arc[center].begin(), arc[center].end(), arcPt{v, qd, x}) - arc[center].begin();
// int res2 = lower_bound(arc[center].begin(), arc[center].end(), arcPt{v, qd+4, x}) - arc[center].begin();
// if (1==sel) return res1 >= after ? res1 : res2;
// return res2 <= after ? res2 : res1;
// }signed main() {ios::sync_with_stdio(0); cin.tie(0);cin >> n;for (int i=0; i<n; ++i) {cin >> pt[i].x >> pt[i].y;}vector<int> idd(n);for (int i=0; i<n; ++i) idd[i] = i;vector<int> convh = makeConvh(idd);for (int x : convh) onConvh[x] = true;areaConvh = getConvhArea(convh);// printf("areaConvh : %lld\n", areaConvh);for (int i=0; i<n; ++i) {for (int j=0; j<n; ++j) if (j!=i) {Pt v = pt[j]-pt[i];int qd = v.quad();arc[i].push_back({v, qd, j});arc[i].push_back({v, qd+4, j});}sort(arc[i].begin(), arc[i].end());}// for (int i=0; i<n; ++i) {// printf("arc[%lld]:", i);// for (auto [v, qd, x] : arc[i]) printf(" %lld", x);// puts("");// }for (int i=0; i<n; ++i) if (!onConvh[i]) {int L = 0;while (!onConvh[arc[i][L].id]) ++L;int bg = arc[i][L].id; int cnt = 0;// int rcnt = 0;// int rarea = 0;for (int j=L+1; j<arc[i].size(); ++j) {++cnt;if (!onConvh[arc[i][j].id]) continue;// printf("i=%lld j=%lld crs=%lld\n", i, j, cross(i, arc[i][L].id, arc[i][j].id));// rarea += cross(i, arc[i][L].id, arc[i][j].id);ans = (ans + (areaConvh - cross(i, arc[i][L].id, arc[i][j].id) + MOD)%MOD * cnt % MOD)%MOD;// rcnt += cnt;cnt = 0;L = j;if (arc[i][L].id == bg) break;}// printf("rcnt = %lld\n", rcnt);// printf("rarea = %lld\n", rarea);}for (int i=0; i<n; ++i) if (!onConvh[i]) {int L = 0;while (!onConvh[arc[i][L].id]) ++L;int bg = arc[i][L].id; tp = -1; stk[++tp] = arc[i][L].id;int sum = (pt[i].crs(pt[arc[i][L].id]) %MOD + MOD)%MOD;// printf("333333\n");// vector<int> dbg = vector<int>({i, arc[i][L].id});for (int j=L+1; j<arc[i].size(); ++j) {if (onConvh[arc[i][j].id]) {if (bg == arc[i][j].id) break;L = j;tp = -1; stk[++tp] = arc[i][L].id;sum = (pt[i].crs(pt[arc[i][L].id]) %MOD + MOD)%MOD;// printf("i=%lld L=%lld id[L]=%lld sum=%lld\n", i, L, arc[i][L].id, sum);// printf("2222\n");// dbg = vector<int>({i, arc[i][L].id});continue;}// printf("i=%lld, j=%lld sum=%lld\n", i, j, sum);while (tp>0 && (pt[stk[tp]]-pt[stk[tp-1]]).crs(pt[arc[i][j].id]-pt[stk[tp-1]]) <= 0) {sum = (sum - (pt[stk[tp-1]].crs(pt[stk[tp]])%MOD) + MOD)%MOD;// printf("11111\n");--tp;}// dbg.push_back(arc[i][j].id);// printf("tp=%lld id[j]=%lld sum=%lld\n", stk[tp].ft, arc[i][j].id, sum);stk[++tp] = arc[i][j].id;sum = (sum + (pt[stk[tp-1]].crs(pt[stk[tp]])%MOD) + MOD)%MOD;ans = (ans + (sum + pt[stk[tp]].crs(pt[i])%MOD + MOD)%MOD)%MOD;// printf("dbg:"); for (int x : dbg) printf(" %lld", x); puts("");// auto dbgconvh = makeConvh(dbg); printf("dbgconvh:"); for (int x : dbgconvh) printf(" %lld", x); puts("");// printf("sum=%lld convh=%lld\n", sum, sum + pt[stk[tp].ft].crs(pt[i]));// printf("i=%lld j=%lld id[j]=%lld L=%lld id[L]=%lld convh=%lld convh_ans=%lld\n", i, j, arc[i][j].id, L, arc[i][L].id, (sum + pt[stk[tp]].crs(pt[i])), getConvhArea(makeConvh(dbg)));// assert((sum + pt[stk[tp]].crs(pt[i])) == getConvhArea(makeConvh(dbg)));}}// printf("44444\n");for (int i=0; i<n; ++i) if (!onConvh[i]) {int L = arc[i].size() - 1;while (!onConvh[arc[i][L].id]) --L;int bg = arc[i][L].id;tp = -1; stk[++tp] = arc[i][L].id;int sum = (pt[arc[i][L].id].crs(pt[i]) %MOD + MOD)%MOD;for (int j=L-1; j>=0; --j) {if (onConvh[arc[i][j].id]) {if (bg == arc[i][j].id) break;L = j;tp = -1; stk[++tp] = arc[i][L].id;sum = (pt[arc[i][L].id].crs(pt[i])%MOD + MOD)%MOD;continue;}while (tp>0 && (pt[arc[i][j].id]-pt[stk[tp-1]]).crs(pt[stk[tp]]-pt[stk[tp-1]]) <= 0) {sum = (sum - pt[stk[tp]].crs(pt[stk[tp-1]])%MOD + MOD)%MOD;--tp;}stk[++tp] = arc[i][j].id;sum = (sum + pt[stk[tp]].crs(pt[stk[tp-1]])%MOD + MOD) % MOD;ans = (ans + (sum + pt[i].crs(pt[stk[tp]])%MOD + MOD)%MOD ) % MOD;// printf("i=%lld j=%lld id[j]=%lld L=%lld id[L]=%lld convh=%lld\n", i, j, arc[i][j].id, L, arc[i][L].id, (sum + pt[i].crs(pt[stk[tp]])));}}cout << ans << '\n';return 0;
}
F. Money Game 2
感觉很显然的一个题,不知道为啥过的人不算很多
首先观察到对于位置 \(i\),其实就等价于在其左边找 \(x\) 个数,右边找 \(y\) 个数,然后按照顺序传递过来,找到满足 \(x+y<n\) 且最优的方案
一个直观的想法是求出 \(L(i,j)\) 表示位置 \(i\) 左边找了 \(j\) 个数带来的贡献,右侧同理,这样复杂度显然有点爆炸
但仔细思考会发现对于 \(L(i,\cdot)\),本质不同的值只有 \(O(\log a_i)\) 种,且值相同的保留 \(j\) 最小的即可
同时 \(L(i+1,\cdot)\) 的值可以由 \(L(i,\cdot)\) 递推而来,而 \(L(1,\cdot)\) 的值只需要暴力二分找到分界点即可,总复杂度 \(O(n\log a_i)\)
#include<cstdio>
#include<iostream>
#include<vector>
#define int long long
#define RI register int
#define CI const int&
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pi;
const int N=500005;
int t,n,a[N]; vector <pi> L[N],R[N];
inline int calcL(CI k)
{int ret=0; for (RI i=n-k+1;i<=n;++i) ret+=a[i],ret/=2;return ret;
}
inline int calcR(CI k)
{int ret=0; for (RI i=k;i>=1;--i) ret+=a[i],ret/=2;return ret;
}
signed main()
{for (scanf("%lld",&t);t;--t){scanf("%lld",&n);for (RI i=1;i<=n;++i) scanf("%lld",&a[i]);L[1].push_back(pi(0,0)); int tar=calcL(n-1);while (L[1].back().se!=tar){int l=L[1].back().fi+1,r=n-1,mid,ret=-1;while (l<=r){int mid=l+r>>1;if (calcL(mid)!=L[1].back().se) ret=mid,r=mid-1; else l=mid+1;}L[1].push_back(pi(ret,calcL(ret)));}for (RI i=2;i<=n;++i){L[i].push_back(pi(0,0));for (auto [num,val]:L[i-1]){int nval=(val+a[i-1])/2;if (num+1<=n-1&&nval!=L[i].back().se)L[i].push_back(pi(num+1,nval));}}R[n].push_back(pi(0,0)); tar=calcR(n-1);while (R[n].back().se!=tar){int l=R[n].back().fi+1,r=n-1,mid,ret=-1;while (l<=r){int mid=l+r>>1;if (calcR(mid)!=R[n].back().se) ret=mid,r=mid-1; else l=mid+1;}R[n].push_back(pi(ret,calcR(ret)));}for (RI i=n-1;i>=1;--i){R[i].push_back(pi(0,0));for (auto [num,val]:R[i+1]){int nval=(val+a[i+1])/2;if (num+1<=n-1&&nval!=R[i].back().se)R[i].push_back(pi(num+1,nval));}}for (RI i=1;i<=n;++i){int k=R[i].size()-1,ret=0;for (RI j=0;j<L[i].size();++j){while (k>=0&&L[i][j].fi+R[i][k].fi>n-1) --k;if (k>=0) ret=max(ret,L[i][j].se+R[i][k].se);}printf("%lld%c",ret+a[i]," \n"[i==n]);}for (RI i=1;i<=n;++i) L[i].clear(),R[i].clear();}return 0;
}
G. Yelkrab
题都没看,被徐神秒了
#include <bits/stdc++.h>using u64 = uint64_t;std::vector<int> hkr[1000006];struct node_t {int out[26];int64_t count;
} node[1000006];void work() {int n; std::cin >> n;u64 ans = 0;size_t sum_of_pig = 0;std::vector<std::string> pig(n);for(auto &pig: pig) std::cin >> pig, sum_of_pig += pig.size();std::vector<u64> ap(sum_of_pig + 1, 0);int O = 0;memset(node + O, 0, sizeof(node_t));int i = 0;for(auto &&pig: pig) {int cur = 0;for(auto c: pig) {int u = c - 'a';if(!node[cur].out[u]) {O += 1;memset(node + O, 0, sizeof(node_t));node[cur].out[u] = O;}cur = node[cur].out[u];int cc = ++node[cur].count;for(auto hkr: hkr[cc]) {ans ^= ap[hkr];ap[hkr] += hkr;ans ^= ap[hkr];}}std::cout << ans << char(++i == n ? 10 : 32);}
}int main() {for(int i = 1; i <= 1000000; ++i) for(int j = i; j <= 1000000; j += i)hkr[j].emplace_back(i);int T; std::cin >> T; while(T--) work();return 0;
}
H. Mah-jong
首先考虑怎么快速判断一个牌的数量向量 \((c_1,c_2,\dots,c_8)\) 是否可以胡牌,其中 \(c_i\) 表示值为 \(i\) 的牌的数量
不难想到只要枚举所有可能的吃的数量,剩下的只要满足所有牌剩余数量为 \(3\) 的倍数即可
而一种吃的数量只有 \(0,1,2\) 种,因此大力枚举的复杂度为 \(3^6\),可以接受
统计时维护前缀的牌的数量向量,但直接用模意义下相减的方法会出现问题,即区间内可能没有足够的牌用来构建吃
因此还需要顺带维护出合法的左端点能到达的最大值,在这个地方统计答案即可
#include<cstdio>
#include<iostream>
#include<vector>
#include<array>
#define RI register int
#define CI const int&
using namespace std;
typedef array <int,8> st;
const int N=100005,S=6561;
int t,pw3[10],n,a[N],bkt[S];
vector <st> chows; vector <int> mdy[N];
inline void DFS(CI p,st& cur)
{if (p==6) return (void)(chows.push_back(cur));for (RI i=0;i<3;++i){for (RI j=0;j<3;++j) cur[p+j]+=i;DFS(p+1,cur);for (RI j=0;j<3;++j) cur[p+j]-=i;}
}
int main()
{pw3[0]=1; for (RI i=1;i<8;++i) pw3[i]=pw3[i-1]*3;st tmp; for (RI i=0;i<8;++i) tmp[i]=0; DFS(0,tmp);// printf("size of chows = %d\n",(int)chows.size());for (scanf("%d",&t);t;--t){scanf("%d",&n); vector <int> rollback;for (RI i=1;i<=n;++i) scanf("%d",&a[i]),--a[i];auto trs=[&](const st& tmp){int mask=0;for (RI i=0;i<8;++i) mask+=tmp[i]%3*pw3[i];return mask; };st pfx; for (RI i=0;i<8;++i) pfx[i]=0;vector <int> num[10];for (RI i=1;i<=n;++i){++pfx[a[i]]; num[a[i]].push_back(i);for (auto cur:chows){bool flag=1;for (RI j=0;j<8;++j)if (pfx[j]<cur[j]) { flag=0; break; }if (!flag) continue;int pos=i; for (RI j=0;j<8;++j)if (cur[j]) pos=min(pos,num[j][(int)num[j].size()-cur[j]]);int mask=0; for (RI j=0;j<8;++j)mask+=(pfx[j]%3-cur[j]%3+3)%3*pw3[j];// printf("i = %d, mask = %d\n",i,mask);mdy[pos-1].push_back(mask);}}for (RI i=0;i<8;++i) pfx[i]=0;int mask=trs(pfx); ++bkt[mask]; rollback.push_back(mask);long long ans=0;for (RI i=1;i<=n;++i){for (auto mask:mdy[i-1]) ans+=bkt[mask];++pfx[a[i]];int mask=trs(pfx); ++bkt[mask]; rollback.push_back(mask);}printf("%lld\n",ans);for (auto x:rollback) bkt[x]=0;for (RI i=0;i<=n;++i) mdy[i].clear();}return 0;
}
K. LR String
徐神开场写的,应该是个签
#include <bits/stdc++.h>void work() {std::string original; std::cin >> original;std::vector<std::pair<char, int>> ori;bool oL = (original.front() == 'L'),oR = (original.back() == 'R');original = original.substr(oL, original.size() - oL - oR);for(int l = 0, r = 0; l < original.size(); l = r) {while(r < original.size() && original[l] == original[r])r++;ori.emplace_back(original[l], r - l);}int T; std::cin >> T; while(T--) {std::string s; std::cin >> s;if(oL && s.front() != 'L') {std::cout << "NO\n";continue;}if(oR && s.back() != 'R') {std::cout << "NO\n";continue;}s = s.substr(oL, s.size() - oL - oR);int i = 0, p1 = 0, p2 = 0;while(i < s.size() && p1 < ori.size()) {if(p2 == ori[p1].second || s[i] != ori[p1].first) {p1++; p2 = 0;} else {i++; p2++;}}std::cout << (i == s.size() ? "YES\n" : "NO\n");}return ;
}int main() {std::ios::sync_with_stdio(false);int T; std::cin >> T; while(T--) work();return 0;
}
L. Flipping Paths
首先枚举最终的目标颜色,考虑使用以下贪心策略
对于一条路径,从第一行出发,每次尽量延申到这一行最右边的不对应颜色再向下拐,直到到达最后一行时再一直向右
不难发现这种方法一次会清除一条对角线上的所有不合法颜色,由于对角线数量为 \(n+m-1\),因此可以通过
#include<cstdio>
#include<iostream>
#include<string>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=205;
int t,n,m,a[N][N]; char s[N][N];
inline bool solve(const char& tar)
{for (RI i=1;i<=n;++i) for (RI j=1;j<=m;++j) a[i][j]=(s[i][j]==tar?0:1);vector <string> res;while ((int)res.size()<=400){bool all_zero=1;for (RI i=1;i<=n;++i) for (RI j=1;j<=m;++j)if (a[i][j]) { all_zero=0; break; }if (all_zero) break;string way=""; int y=1;for (RI x=1;x<=n;++x){int rgt=-1;for (RI j=m;j>=1;--j)if (a[x][j]) { rgt=j; break; }if (x==n) rgt=m;while (y<rgt){a[x][y]^=1;way.push_back('R'); ++y;}a[x][y]^=1;if (x!=n) way.push_back('D');}res.push_back(way);}if ((int)res.size()<=400){puts("YES");printf("%d\n",(int)res.size());for (auto way:res) cout<<way<<endl;return true;}return false;
}
int main()
{for (scanf("%d",&t);t;--t){scanf("%d%d",&n,&m);for (RI i=1;i<=n;++i) scanf("%s",s[i]+1);if (solve('W')) continue;if (solve('B')) continue;puts("NO");}return 0;
}
Postscript
由于要准备组会的汇报,剩下看着可做的 J 就懒得补了,开摆就是爽啊