隐藏页面特效

The 2023 ICPC Asia Xi'an Regional Contest

1|0Preface


久违地组队训练一场,不知道打什么就挑了场最近才上 QOJ 的 23 年西安 Regional

作为“声名远扬”的凹包场,还有 O(n3ω)n=5000 的神秘数据,导致这场的 downvote 率奇高

但上 QOJ 的时候数据应该是修复了的,凹包好像 fix 了,bitset 大力出奇迹的题我们队最后 1min 胡了个神秘讨论上去然后直接过了

最后 7 题罚时尾,话说最近不管什么比赛罚时怎么都这么炸


2|0A. An Easy Geometry Problem


发病了,本来早过了,结果发病了线段树 pushup 能写挂直接花了快 2h 来调这个题,还浪费时间写了个拍才看出来

Ai+rAir=kr+b 稍加变形,得到 Ai+rk2×(i+r)=Airk2×(ir)+b

因此我们令 ai=2×Aii×k,bi=2×Aii×k+2×b,则比较是就是判断 a[i+1,i+r] (从前往后)和 b[ir,i1] (从后往前)是否相同

拿个线段树维护下区间向前/向后两个方向的哈希值即可,查询的话直接先二分再在线段树上查,实测再加个双哈希都能跑过

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=200005,mod1=998244353,mod2=1e9+7,S=4e8; int n,q,k,b,a[N],A[N],B[N]; struct Hasher { int x,y; inline Hasher(CI X=0,CI Y=0) { x=X; y=Y; } friend inline bool operator == (const Hasher& A,const Hasher& B) { return A.x==B.x&&A.y==B.y; } friend inline bool operator != (const Hasher& A,const Hasher& B) { return A.x!=B.x||A.y!=B.y; } friend inline Hasher operator + (const Hasher& A,const Hasher& B) { return Hasher((A.x+B.x)%mod1,(A.y+B.y)%mod2); } friend inline Hasher operator - (const Hasher& A,const Hasher& B) { return Hasher((A.x-B.x+mod1)%mod1,(A.y-B.y+mod2)%mod2); } friend inline Hasher operator * (const Hasher& A,const Hasher& B) { return Hasher(1LL*A.x*B.x%mod1,1LL*A.y*B.y%mod2); } }pw[N],sum[N]; const Hasher seed=Hasher(2*S+31,2*S+131); struct ifo { Hasher val; int len; inline ifo(const Hasher& VAL=Hasher(),CI LEN=0) { val=VAL; len=LEN; } }; inline ifo merge_pre(const ifo& L,const ifo& R) { return ifo(L.val*pw[R.len]+R.val,L.len+R.len); } inline ifo merge_suf(const ifo& L,const ifo& R) { return ifo(R.val*pw[L.len]+L.val,L.len+R.len); } class Segment_Tree { private: ifo pre[N<<2],suf[N<<2]; Hasher tag[N<<2]; #define TN CI now=1,CI l=1,CI r=n #define LS now<<1,l,mid #define RS now<<1|1,mid+1,r inline void apply(CI now,const Hasher& mv) { tag[now]=tag[now]+mv; pre[now].val=pre[now].val+mv*sum[pre[now].len-1]; suf[now].val=suf[now].val+mv*sum[suf[now].len-1]; } inline void pushup(CI now) { pre[now]=merge_pre(pre[now<<1],pre[now<<1|1]); suf[now]=merge_suf(suf[now<<1],suf[now<<1|1]); } inline void pushdown(CI now) { if (tag[now]!=Hasher()) apply(now<<1,tag[now]),apply(now<<1|1,tag[now]),tag[now]=Hasher(); } public: inline void build(TN) { pre[now].len=suf[now].len=r-l+1; if (l==r) { pre[now].val=Hasher((A[l]+mod1)%mod1,(A[l]+mod2)%mod2); suf[now].val=Hasher((B[l]+mod1)%mod1,(B[l]+mod2)%mod2); return; } int mid=l+r>>1; build(LS); build(RS); pushup(now); } inline void modify(CI beg,CI end,const Hasher& mv,TN) { if (beg<=l&&r<=end) return apply(now,mv); int mid=l+r>>1; pushdown(now); if (beg<=mid) modify(beg,end,mv,LS); if (end>mid) modify(beg,end,mv,RS); pushup(now); } inline ifo qry_pre(CI beg,CI end,TN) { if (beg<=l&&r<=end) return pre[now]; int mid=l+r>>1; pushdown(now); if (end<=mid) return qry_pre(beg,end,LS); if (beg>mid) return qry_pre(beg,end,RS); return merge_pre(qry_pre(beg,end,LS),qry_pre(beg,end,RS)); } inline ifo qry_suf(CI beg,CI end,TN) { if (beg<=l&&r<=end) return suf[now]; int mid=l+r>>1; pushdown(now); if (end<=mid) return qry_suf(beg,end,LS); if (beg>mid) return qry_suf(beg,end,RS); return merge_suf(qry_suf(beg,end,LS),qry_suf(beg,end,RS)); } #undef TN #undef LS #undef RS }SEG; int main() { // freopen("A.in","r",stdin); freopen("A.out","w",stdout); scanf("%d%d%d%d",&n,&q,&k,&b); for (RI i=1;i<=n;++i) { scanf("%d",&a[i]); A[i]=2*a[i]-k*i; B[i]=2*a[i]-k*i+2*b; } pw[0]=Hasher(1,1); sum[0]=Hasher(1,1); for (RI i=1;i<=n;++i) pw[i]=pw[i-1]*seed; for (RI i=1;i<=n;++i) sum[i]=sum[i-1]+pw[i]; SEG.build(); while (q--) { int opt; scanf("%d",&opt); if (opt==1) { int l,r,v; scanf("%d%d%d",&l,&r,&v); SEG.modify(l,r,Hasher((2*v+mod1)%mod1,(2*v+mod2)%mod2)); } else { int x; scanf("%d",&x); int l=1,r=min(x-1,n-x),ret=0; while (l<=r) { int mid=l+r>>1; if (SEG.qry_pre(x+1,x+mid).val==SEG.qry_suf(x-mid,x-1).val) ret=mid,l=mid+1; else r=mid-1; } printf("%d\n",ret); } // for (RI i=1;i<=n;++i) printf("%d%c",SEG.qry_pre(i,i).val.x," \n"[i==n]); // for (RI i=1;i<=n;++i) printf("%d%c",SEG.qry_suf(i,i).val.x," \n"[i==n]); } return 0; }

3|0E. Dominating Point


我勒个 4h59min 绝杀啊,感觉找到切入点就不难的一个题

考虑找到图中出度最多的点 A,由竞赛图的性质,这个点一定是 Dominating Point,具体证明可以看官方题解

此时不妨把剩下的点分为两个集合,A 出边的点集记为 S1A 入边的点集记为 S2

考虑若 S2 为空则显然无解,否则我们把 S2 中剩下的点的导出子图中,出度最多的点 B 找出,显然这个点也一定是 Dominating Point

现在问题就是第三个 Dominating Point 怎么找,题解给出的方法需要讨论 B 是否是中心点,但当时最后徐神告诉我令 S3B 入边的点集,则 S3 中剩下的点的导出子图中,出度最多的点 C 一定合法

具体证明可以看官方题解,这题主要是画画图讨论一下就不难猜到结论

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=5005; int n,deg[N],vis[N],key[N]; char s[N][N]; int main() { scanf("%d",&n); for (RI i=1;i<=n;++i) scanf("%s",s[i]+1); for (RI i=1;i<=n;++i) for (RI j=1;j<=n;++j) if (s[i][j]=='1') ++deg[i]; int A=1,B=-1,C=-1; for (RI i=2;i<=n;++i) if (deg[i]>deg[A]) A=i; if (deg[A]==n-1) return puts("NOT FOUND"),0; for (RI i=1;i<=n;++i) if (s[i][A]=='1') vis[i]=1,B=i; if (B==-1) return puts("NOT FOUND"),0; for (RI i=1;i<=n;++i) deg[i]=0; for (RI i=1;i<=n;++i) for (RI j=1;j<=n;++j) if (vis[i]&&vis[j]&&s[i][j]=='1') ++deg[i]; for (RI i=1;i<=n;++i) if (vis[i]&&deg[i]>deg[B]) B=i; for (RI i=1;i<=n;++i) if (s[i][B]=='1') key[i]=1,C=i; for (RI i=1;i<=n;++i) deg[i]=0; for (RI i=1;i<=n;++i) for (RI j=1;j<=n;++j) if (key[i]&&key[j]&&s[i][j]=='1') ++deg[i]; for (RI i=1;i<=n;++i) if (key[i]&&deg[i]>deg[C]) C=i; if (C==-1) return puts("NOT FOUND"),0; return printf("%d %d %d",A,B,C),0; }

4|0F. An Easy Counting Problem


这题我基本没参与全程队友想+写的

做法大致就是通过卢卡斯定理,会推出一个类似于背包的转移形式

但由于数据范围出的比较 shit,因此需要先找原根把下标乘法转化为下标加法,然后用 NTT 优化循环卷积

#include <bits/stdc++.h> using llsi = long long signed int; constexpr llsi mod = 998244353; int C[5000][5000]; llsi hkr[5000], ug[20000], ssk[20000], vv[20000]; void prep(int n); void mult(llsi *a, llsi *b, int); void NTT(llsi*, int); int Log[5000], iLog[5000]; int find_root(int p) { int i; for(i = 1; i < p; ++i) { int cur = i, logCur = 1; while(cur != 1) cur = cur * i % p, logCur += 1; if(logCur == p - 1) break; } assert(i < p); // impossible int cur = 1, logCur = 0; do { iLog[Log[cur] = logCur] = cur; cur = cur * i % p; logCur += 1; } while(cur != 1); return i; } void work() { std::string k; int p, x; std::cin >> k >> p >> x; for(int i = 0; i < p; ++i) hkr[C[i][0] = 1]++; for(int i = 1; i < p; ++i) for(int j = 1; j <= i; ++j) hkr[C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % p]++; // std::cerr << "root = " << find_root(p) << char(10); find_root(p); for(int i = 1; i < p; ++i) ug[Log[i]] = hkr[i]; // for(int i = 0; i < p; ++i) std::cout << hkr[i] << char(i == p - 1 ? 10 : 32); // for(int i = 0; i < p - 1; ++i) std::cout << ug[i] << char(i == p - 2 ? 10 : 32); prep(2 * (p - 1)); std::reverse(k.begin(), k.end()); ssk[0] = 1; for(auto ch: k) { if(ch == '1') mult(ssk, ug, p - 1); mult(ug, ug, p - 1); } // for(int i = 0; i < p - 1; ++i) std::cout << ssk[i] << char(i == p - 2 ? 10 : 32); std::cout << ssk[Log[x]] << char(10); } int main() { std::ios::sync_with_stdio(false); int n; n = 1; while(n--) work(); return 0; } int n, otae[20000]; constexpr llsi ksm(llsi a, llsi b) { llsi c = 1; while(b) { if(b & 1) c = c * a % mod; a = a * a % mod; b >>= 1; } return c; } constexpr llsi g = 3, gi = ksm(g, mod - 2); void prep(int _n) { n = std::bit_ceil(uint32_t(_n)); otae[0] = 0; for(int i = 1; i < n; ++i) otae[i] = (otae[i >> 1] >> 1) | ((n >> 1) & -(i & 1)); } void NTT(llsi *a, int on) { for(int i = 0; i < n; ++i) if(otae[i] > i) std::swap(a[i], a[otae[i]]); for(int d = 1; d < n; d <<= 1) { llsi W = ksm(on > 0 ? g : gi, (mod - 1) / (d << 1)); for(int j = 0; j < n; j += 2 * d) { llsi w = 1; for(int k = 0; k < d; ++k, w = w * W % mod) { llsi x = a[j + k], y = w * a[j + d + k] % mod; a[j + k] = (x + y) % mod; a[j + d + k] = (mod + x - y) % mod; } } } if(on < 0) { llsi inv = ksm(n, mod - 2); for(int i = 0; i < n; ++i) a[i] = a[i] * inv % mod; } } void mult(llsi *a, llsi *_b, int p) { llsi *b = vv; memcpy(b, _b, sizeof(llsi) * n); // for(int i = p; i < n; ++i) b[i] = 0; NTT(a, 1), NTT(b, 1); for(int i = 0; i < n; ++i) a[i] = a[i] * b[i] % mod; NTT(a, -1); for(int i = n - 1; i >= p; --i) a[i - p] = (a[i] + a[i - p]) % mod, a[i] = 0; }

5|0G. An Easy Math Problem


签到题,本来现场暴力可过的,然后我们 VP 的时候被 extra test 干掉了,最后 3h 才会正解

考虑枚举 n 的一对约数 p,q,显然为了去重我们只在 gcd(p,q)=1 时计数

考虑将 n 质因数分解,令 n=pici,不妨考虑 pi 单个质因数的贡献

由于 p,q 互质,因此 pi 的指数上二者取值的 min 必须为 0,因此有 2ci+1 种方案

在不考虑 pq 时方案数显然为 (2ci+1),而有序对的答案为 (2ci+1)+12

#include<cstdio> #include<iostream> #define int long long #define RI register int #define CI const int& using namespace std; int t,n; signed main() { for (scanf("%lld",&t);t;--t) { scanf("%lld",&n); int ans=1; for (RI p=2;p*p<=n;++p) if (n%p==0) { int c=0; while (n%p==0) ++c,n/=p; ans*=(2LL*c+1); } if (n>1) ans*=3; printf("%lld\n",(ans+1)/2LL); } return 0; }

6|0H. Elimination Series Once More


暴力枚举每个人最多能赢的轮次,其实就是看当前某个区间内小于某个数的数量

从小到大处理每个 {ai},然后拿一个类似线段树的东西维护下每层的区间和即可,复杂度 O(2n×n)

#include<cstdio> #include<iostream> #include<vector> #include<algorithm> #define RI register int #define CI const int& using namespace std; const int N=(1<<20)+5; int n,k,all,a[N],pos[N],ans[N],sum[N<<2]; inline void update(vector <int>& vec,CI pos,CI now=1,CI l=1,CI r=all) { ++sum[now]; vec.push_back(sum[now]); if (l==r) return; int mid=l+r>>1; if (pos<=mid) update(vec,pos,now<<1,l,mid); else update(vec,pos,now<<1|1,mid+1,r); } int main() { scanf("%d%d",&n,&k); all=1<<n; for (RI i=1;i<=all;++i) scanf("%d",&a[i]),pos[a[i]]=i; for (RI i=1;i<=all;++i) { int x=pos[i]; vector <int> vec; update(vec,x); reverse(vec.begin(),vec.end()); // printf("i = %d\n",i); // for (auto x:vec) printf("%d ",x); putchar('\n'); int ret=n; for (RI r=0;r<vec.size();++r) { if ((1<<r)<=i&&(1<<r)-vec[r]<=k) continue; ret=r-1; break; } ans[x]=ret; } for (RI i=1;i<=all;++i) printf("%d ",ans[i]); return 0; }

7|0I. Max GCD


赛后看题解补的,感觉还是挺小清新的

考虑固定答案时,有意义的三元组 (i,j,k) 一定满足 i,j 作为其倍数出现的位置相邻,k 是符合要求且最靠近 j 的位置,这样的三元组个数是 O(n×σ(ai))

如何快速求出这些三元组呢,枚举中间的值 ai 的所有约数 d,设上一次出现 d 的倍数的位置为 lstd,则所有 k2×ilstd 的位置中,第一个满足 akd 的倍数就是需要的

把信息挂在 2×ilstd 上然后从后往前扫描一遍即可求出所有三元组,然后把询问离线跑一个二维数点即可

注意这题修改有 O(n×σ(ai)) 次但查询只有 O(q) 次,因此拿个分块维护信息可以做到 O(n×σ(ai)+q×n) 的复杂度

#include<cstdio> #include<iostream> #include<vector> #include<cmath> #define RI register int #define CI const int& using namespace std; const int N=1e6+5; typedef pair <int,int> pi; int n,q,a[N],lst[N],ans[N],sz,bel[N],val[N],tag[N]; vector <int> frac[N]; vector <pi> vec[N],mdy[N],ques[N]; inline void init(CI n) { for (RI i=1;i<=n;++i) for (RI j=i;j<=n;j+=i) frac[j].push_back(i); } inline void modify(CI p,CI mv) { val[p]=max(val[p],mv); tag[bel[p]]=max(tag[bel[p]],mv); } inline int query(CI l,CI r) { int ret=0; if (bel[l]==bel[r]) { for (RI i=l;i<=r;++i) ret=max(ret,val[i]); return ret; } for (RI i=l;i<=bel[l]*sz;++i) ret=max(ret,val[i]); for (RI i=(bel[r]-1)*sz+1;i<=r;++i) ret=max(ret,val[i]); for (RI i=bel[l]+1;i<=bel[r]-1;++i) ret=max(ret,tag[i]); return ret; } int main() { scanf("%d%d",&n,&q); init(1e6); for (RI i=1;i<=n;++i) scanf("%d",&a[i]); for (RI i=1;i<=n;++i) { for (auto d:frac[a[i]]) { if (!lst[d]) continue; vec[2*i-lst[d]].push_back({lst[d],d}); } for (auto d:frac[a[i]]) lst[d]=i; } for (RI i=1;i<=1000000;++i) lst[i]=0; for (RI i=n;i>=1;--i) { for (auto d:frac[a[i]]) lst[d]=i; for (auto [l,g]:vec[i]) { if (!lst[g]) continue; mdy[lst[g]].push_back({l,g}); } } // for (RI i=1;i<=n;++i) for (auto [l,g]:mdy[i]) // printf("l = %d, r = %d, g = %d\n",l,i,g); for (RI i=1;i<=q;++i) { int l,r; scanf("%d%d",&l,&r); ques[r].push_back({l,i}); } sz=(int)sqrt(n); for (RI i=1;i<=n;++i) bel[i]=(i-1)/sz+1; for (RI i=1;i<=n;++i) { for (auto [l,g]:mdy[i]) modify(l,g); for (auto [l,id]:ques[i]) ans[id]=query(l,i); } for (RI i=1;i<=q;++i) printf("%d\n",ans[i]); return 0; }

8|0L. Prism Palace


祁神写的几何,我题目都没看

#include<bits/stdc++.h> using namespace std; #define int long long using LD = long double; const LD PI = acosl(-1.0L); struct Pt { int x, y; int crs(const Pt &b) const {return x*b.y-y*b.x;} int dot(const Pt &b) const {return x*b.x+y*b.y;} Pt operator-(const Pt &b) const {return Pt{x-b.x, y-b.y};} 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; } bool operator<(const Pt &b) const { int qda = quad(), qdb = b.quad(); return (qda!=qdb ? qda < qdb : crs(b)>=0); } }; const int N = 2e5+5; int n; Pt pt[N], vv[N]; signed main() { ios::sync_with_stdio(0); cin.tie(0); cout << setiosflags(ios::fixed) << setprecision(10); cin >> n; for (int i=0; i<n; ++i) { int x, y; cin >> x >> y; pt[i] = {x, y}; } if (n==3) { cout << 1.0L << '\n'; return 0; } for (int i=0; i<n; ++i) vv[i] = pt[i] - pt[(i-1+n)%n]; sort(vv, vv+n); LD ans = 0.0L; for (int i=0; i<n; ++i) { Pt v1 = vv[(i-1+n)%n]; Pt v2 = vv[(i+1)%n]; if (v2.crs(v1) > 0) { LD th = atan2l(v2.crs(v1), v2.dot(v1)); ans = (PI-th) / PI; break; } } cout << ans << '\n'; return 0; }

9|0N. Python Program


小清新模拟,难点在于处理输入

由于数据范围不大,我们第一层循环枚举,第二层循环推一个等差数列求和的式子即可

#include<cstdio> #include<iostream> #include<string> #include<cctype> #define RI register int #define CI const int& using namespace std; const int INF=1e9; int a,b,c,d,e,f; int main() { string s; getline(cin,s); getline(cin,s); int p=0; while (s[p]!='(') ++p; ++p; while (isdigit(s[p])) a=a*10+s[p++]-'0'; ++p; while (isdigit(s[p])) b=b*10+s[p++]-'0'; ++p; if (s[p]==':') c=1; else { int flag=1; if (s[p]=='-') ++p,flag=-1; while (isdigit(s[p])) c=c*10+s[p++]-'0'; ++p; c*=flag; } getline(cin,s); p=0; while (s[p]!='(') ++p; ++p; if (isalpha(s[p])) d=INF,p+=2; else { while (isdigit(s[p])) d=d*10+s[p++]-'0'; ++p; } if (isalpha(s[p])) e=INF,p+=2; else { while (isdigit(s[p])) e=e*10+s[p++]-'0'; ++p; } if (s[p]==':') f=1; else if (isalpha(s[p])) f=INF,p+=2; else { int flag=1; if (s[p]=='-') ++p,flag=-1; while (isdigit(s[p])) f=f*10+s[p++]-'0'; ++p; f*=flag; } getline(cin,s); long long ans=0; // printf("%d %d %d\n%d %d %d\n",a,b,c,d,e,f); auto calc=[&](CI a,CI b,CI c) { // printf("calc(%d,%d,%d) = ",a,b,c); if (c>0) { if (a>=b) return 0LL; int k=max((b-a-1)/c,0); // printf("%lld\n",1LL*(2*a+1LL*k*c)*(k+1)/2LL); return 1LL*(2*a+1LL*k*c)*(k+1)/2LL; } else { if (a<=b) return 0LL; int k=max((a-b-1)/(-c),0); // printf("%lld\n",1LL*(2*a+1LL*k*c)*(k+1)/2LL); return 1LL*(2*a+1LL*k*c)*(k+1)/2LL; } }; if (c>0) { for (RI i=a;i<b;i+=c) if (d==INF) ans+=calc(i,e,f); else if (e==INF) ans+=calc(d,i,f); else if (f==INF) ans+=calc(d,e,i); else ans+=calc(d,e,f); } else { for (RI i=a;i>b;i+=c) if (d==INF) ans+=calc(i,e,f); else if (e==INF) ans+=calc(d,i,f); else if (f==INF) ans+=calc(d,e,i); else ans+=calc(d,e,f); } printf("%lld",ans); return 0; }

10|0Postscript


今天举行的南京站同校已经有队伍校排 Rank10 进入出线队列了,这下感觉压力有点大啊


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/18524079.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(622)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
历史上的今天:
2022-11-03 Codeforces Round #831 (Div. 1 + Div. 2)
点击右上角即可分享
微信分享提示