The 3rd Universal Cup 做题记录
Stage 0 - Stage 9:The 3rd Universal Cup 做题记录 (1)
Stage 10 - Stage 19:The 3rd Universal Cup 做题记录 (2)]]
Stage 10: West Lake ACDGHKL
Stage 11: Sumiyosi ABCHIKLMO
Stage 12: Qinhuangdao ABCDEGHKLM
Stage 13: Sendai BCDEFGHIKMO
Stage 14: Harbin ABCDEGJKLM
Stage 15: Chengdu ABCDEGIJKL
Stage 16: Nanjing BCEFGIJKM
Stage 17: Jinan ABCDEFIJL
The 3rd Universal Cup. Stage 10: West Lake
A. Italian Cuisine
复制一遍,枚举 维护右端点 。要求 到过 的直线距离大于 或 等于 且交点不在线段上,即 和 至少一个为钝角,即两个向量的数量积小于零。要求 的夹角和 的夹角同正负,即两个向量的叉积同正负。三点坐标求面积 。
C. Permutation
大概在 级别。线段树维护区间 有什么数,每次随机取出两个数,将左半边设为 ,右半边设为 询问。如果 可以将 放入左右儿子,否则如果这是第一次,就把 放回去重来,否则一定找得到一个数 ,将某半边设为 再问一遍。理论上是 ,加上剪枝次数就差不多刚好了,偶尔会超。
upd:正解是已知 在同半边后并查集将 当作一个点继续做。
D. Collect the Coins
按 排序,二分答案 。设 为前 个询问,另一个人在询问 。如果 , 继承 。然后再考虑 更新 。拆绝对值有 且 。维护对应的 和 即可。
G. Stop the Castle 2
优先破坏两对车的障碍。枚举障碍,如果一个障碍能破坏一横一竖两对车,连边二分图匹配。其余的一次断一队车,set 维护是否存在一横或一竖相邻的车。
H. Intersection of Paths
按 从大往小回答,不断加入两端的 siz 较小值符合 的边。动态修改边权求直径。直径表示为欧拉序上连续的 的 ,线段树维护区间加和式子的部分的最大值。
K. Palindromic Polygon
复制一遍,设 为区间 中选了 且是回文串、除了 是回文串,除了 是回文串的最大面积。
L. Cosmic Travel
建 trie 树,对于每个节点求出异或上所有 的 前 的和,可以由左右儿子推知。询问时在 覆盖整个 时回答。复杂度 。
The 3rd Universal Cup. Stage 11: Sumiyosi
A. Welcome to NPCAPC
矩阵快速幂,预处理 处的矩阵。
B. Some Sum of Subset
倒序背包,在第一次 处统计答案。预处理有 个可以任选对 的贡献系数。
C. Solve with Friends
枚举 A 做几个,预处理前缀和,按 排序依次把 B 做改为 A 做。
H. Music Game
期望做 次,每个前缀都有概率失败,失败有代价。按 排序。
I. Left Equals Right
直接 dp 前 个选 个和为 ,左右随便排列。
K. Peace with Magic
设 为前 个, 的代价。前后缀 min 转移。
L. Construction of Town
先连成菊花,然后 中两两连边。
O. New School Term
从后往前加,维护拓展域并查集。每次合并后,需要能凑出和为 。拓展域的限制为 只能选一个,维护 的 01 背包。bitset 二进制分组,本质不同数 级别。复杂度 。
The 3rd Universal Cup. Stage 12: Qinhuangdao
A. Balloon Robot
可以找到一个起点 使得从 出发 时在 。贡献形如区间加一次函数,且只能在所有的 和 处取得最小值。离散化后差分。
B. Expected Waiting Time
设 为长为 的合法括号序列数,即卡特兰数 。枚举 , 作为左端点的方案数为 ,其他情况 作为右端点。是一个前缀的形式,预处理前缀和。总代价除以方案数得期望。
D. Graph Generator
合并形如一颗树,有 。令 最小的可行的 作为 的父亲。从下往上检查答案,要求 的出边在 子树中的数量等于 。
E. String of CCPC
加一个字符最多贡献为 ,最多只加一个字符。
G. Numbers
按二进制从高位到低位贪心,使用 python。
H. Prime Set
除了 特例外,奇数向偶数连边,二分图匹配。先不加 求出最大匹配,再加上 匹配,最后剩下的 两两匹配。
K. Diversity and Variance
令 为保留的数。特判 。答案一定为一段前缀加一段后缀,值相同的数要选字典序小的。按 和 分别排序。方差拆为 和 相关,求出可能贡献答案的是那些前缀。两个方案的字典序比较时,分类讨论比较两边不重叠的区间的最小值。
L. One-Dimensional Maze
向左走 R
的数量和向右走 L
的数量的 min。
M. Safest Buildings
离原点近的一定不劣。离原点距离小于 的一样优,如果没有这样的点,离原点最近的点最优。
The 3rd Universal Cup. Stage 13: Sendai
B. Topological Sort
枚举 ,找到 的最大 ,要求 到 不能没有边,如果不存在 则为 。
C. Shift Puzzle
乱搞冲过去的。
倒序做把 T 的第 列改为全 ,然后 S 通过第 列不断调整。大常数写法对 小有优势,两种写法拼起来。
D. And DNA
时 , 时不存在 00
或 111
,建图 ,矩乘快速幂维护一个点走 步回到自己的方案数。 为偶数 同奇偶,等于 。否则最下一位与上面位独立,等于 。记搜。
F. Min Nim
为奇或非最小值数量 为奇先手赢。
G. Count Pseudo-Palindromes
对于 , 是跨过 或 的最小/大的数。随机异或哈希,预处理 表示前后缀 的出现次数。并查集从 开始跳没有到过的点到 ,跳到的点是 对 有贡献。哈希表做到 。
H. Maximize Array
贪心取最大的且下标最小的。维护模 的位置的后缀 min。
I. Colored Complete Graph
先问 和 。维护红点集合和蓝点集合,每次各取出一个点 ,如果 为红就删掉 ,否则删掉 ,直到一个集合为空。 次。
K. Random Mex
来自 AT 官方题解。
考虑组合意义:有 个盒子,给第 个盒子放一个球,找到一个编号小于最小空盒子编号的盒子将其中所有的球放入 号盒子的方案数。即 ,即为答案。反过来, 个球分给 个盒子,第 个盒子不为空,存在至少一个空盒子,把第 个盒子中的球放到编号最小的空盒子中。即 。 为第二类斯特林数。
M. Do Not Turn Back
设 为时刻 位置 的答案,容斥掉 和 的情况,特判 不存在一来一去一回。。矩乘快速幂。
O. Sub Brackets
当两个线段交为奇数时存在矛盾。连边求最大独立集,等价于总数减最小覆盖集。
The 3rd Universal Cup. Stage 14: Harbin
A. Build a Computer
类似线段树的结构,按不同长度分别线段树查询,完全包含一个区间就加上一个 个点表示 的结构。只有最短和最长的长度区间进入线段树查询,其余的直接在第一次返回。
B. Concave Hull
最优结构是凸包挖去一个凸包内的点和一条凸包上的边构成的三角形。有贡献的凸包内的点是凸包内的点的凸包上的点,且内层凸包上每个点贡献到外层凸包上的边是单调的。叉积计算。
int n;
struct poi{
int x,y;
bool operator<(const poi&tmp)const{return x<tmp.x||(x==tmp.x&&y<tmp.y);}
poi operator-(const poi&tmp)const{return {x-tmp.x,y-tmp.y};}
int operator*(const poi&tmp)const{return x*tmp.y-y*tmp.x;}
};
vector<poi> a;
vector<int> tubao(vector<poi> a){
if(a.size()==1)return vector<int>({0});
if(a.size()==2)return vector<int>({0,1});
vector<int> res;
for(int i=0;i<a.size();i++){
while(res.size()>1){
int lst=res.back();res.pop_back();
if((a[lst]-a[res.back()])*(a[i]-a[lst])>=0){res.pb(lst);break;}
}
res.pb(i);
}
int sz=res.size();
for(int i=a.size()-2;~i;i--){
while(res.size()>sz){
int lst=res.back();res.pop_back();
if((a[lst]-a[res.back()])*(a[i]-a[lst])>=0){res.pb(lst);break;}
}
res.pb(i);
}
res.pop_back();
return res;
}
bool vis[maxn];
void work(){
n=read();a.resize(n);
for(int i=0;i<n;i++)a[i]={read(),read()};
sort(a.begin(),a.end());
vector<int> res=tubao(a);
vector<poi> b,c;
for(int i=0;i<n;i++)vis[i]=0;
for(int i:res)vis[i]=1,b.pb(a[i]);
if(b.size()==n){puts("-1");return ;}
for(int i=0;i<n;i++)if(!vis[i])c.pb(a[i]);
a=c;c.clear();
res=tubao(a);
for(int i:res)c.pb(a[i]);
int m=c.size(),pos=0;
for(int i=1;i<m;i++)if((c[i]-b[0])*(c[i]-b[1])<(c[pos]-b[0])*(c[pos]-b[1]))pos=i;
int ans=inf,sum=0;
for(int i=0;i<b.size();i++)sum+=b[i]*b[(i+1)%b.size()];
for(int i=0;i<b.size();i++){
while((c[pos]-b[i])*(c[pos]-b[(i+1)%b.size()])>(c[(pos+1)%m]-b[i])*(c[(pos+1)%m]-b[(i+1)%b.size()]))pos=(pos+1)%m;
ans=min(ans,(c[pos]-b[i])*(c[pos]-b[(i+1)%b.size()]));
}
printf("%lld\n",sum-ans);
}
C. Giving Directions in Harbin
模拟,特判 是要绕一圈。
D. A Simple String Problem
不妨令 中存在答案字符串的一半及以上,翻转并交换 和 再做一遍。枚举答案长度 ,每隔 取一个点 ,一定能经过答案字符串的一半上的一点,在讨论另一半对应的点在 或 ,二分哈希求两串和 串两个位置的 lcp 和 lcs。
E. Marble Race
按每个球在每个起点出发到零点的时间升序枚举。设 表示有 个球在零点右边的概率,。维护球 经过零点的次数 ,撤销 , 加 ,加入 。
J. New Energy Vehicle
每次优先用下一个充电点近的电池,set 模拟。
K. Farm Management
要么操作最大的 ,收益 。要么操作较小的 ,然后不选这个 ,二分剩下的分到那些地方。
L. A Game On Tree
算方案数。对于 ,贡献为 。维护 ,dfs 时维护贡献。
int n,ans;
int head[maxn],tot;
struct nd{
int nxt,to;
}e[maxn<<1];
void add(int u,int v){e[++tot]={head[u],v};head[u]=tot;}
int siz[maxn],sum[maxn];
void dfs(int u,int fa){
siz[u]=1,sum[u]=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;if(v==fa)continue;
dfs(v,u);
(ans+=2*sum[u]*sum[v])%=mod;
(ans+=2*(n-siz[v])*(n-siz[v])%mod*(sum[v]+mod-siz[v]*siz[v]%mod))%=mod;
(sum[u]+=sum[v])%=mod;siz[u]+=siz[v];
}
(sum[u]+=siz[u]*siz[u])%=mod;
(ans+=siz[u]*siz[u]%mod*(n-siz[u])%mod*(n-siz[u]))%=mod;
}
void work(){
n=read();ans=0;
for(int i=1;i<=n;i++)head[i]=0;tot=0;
for(int i=1;i<n;i++){
int u=read(),v=read();
add(u,v),add(v,u);
}
dfs(1,0);
int ss=(n*(n-1)/2)%mod;ss=ss*ss%mod;
ans=ans*ksm(ss)%mod;
printf("%lld\n",ans);
}
The 3rd Universal Cup. Stage 15: Chengdu
A. Arrow a Row
要求最后三个为 >>>
,第一个为 >
,至少有一个 -
。
B. Athlete Welcome Ceremony
表示取多少个 a,b,c 的答案,在三个维度做 次前缀和。
注意到有 这样的无意义询问,对 取 min。
C. Chinese Chess
处理出两两点间不同种棋的最短路。发现询问次数 ,对 分别做。直接枚举决策点,按答案将询问分为几个部分,要求递归下去的子问题也符合能区分。而且发现对于 ,第一步问 就可以了,不会接着往下枚举。
D. Closest Derangement
偶数只有一种情况, 和 交换。
奇数有 种情况, 被换为 或 。重载比较函数排序。分类讨论,比较两边不重叠的部分的一个区间的下标最小值。
int n,k;
int a[maxn],p[maxn];
int id[maxn];
pii mn[20][maxn];
pii que(int l,int r){
if(l>r)return {inf,0};
int k=log2(r-l+1);
return min(mn[k][l],mn[k][r-(1<<k)+1]);
}
bool cmp(int uu,int vv){
int u=uu,v=vv,fl=0;
if(u==v)return 0;
if(u>v)swap(u,v),fl=1;
if(u/2==v/2){
pii pos=que(u-1,v);
return fl^(pos.se&1);
}
int l=(u&1)?u-2:u,r=(v&1)?v:v+1;
pii pos=min({que(l,u-2),que(u,v-1),que(v+1,r)});
if(v&1){
if((pos.se&1)&&pos.se>=u)return fl^1;
return fl;
}
else{
if((pos.se&1)&&pos.se>=u&&pos.se<v)return fl^1;
return fl;
}
}
void work(){
n=read();k=read();
for(int i=1;i<=n;i++)a[i]=read(),p[a[i]]=i,mn[0][a[i]]={i,a[i]};
for(int j=1;j<=18;j++){
for(int i=1;i+(1<<j)-1<=n;i++)mn[j][i]=min(mn[j-1][i],mn[j-1][i+(1<<j-1)]);
}
if(n&1){
if(k>n-1){puts("-1");return ;}
for(int i=2;i<=n;i++)id[i]=i;
sort(id+2,id+n+1,cmp);
int pos=id[k+1];
for(int i=1;i<=((pos&1)?pos-3:pos-2);i+=2)swap(a[p[i]],a[p[i+1]]);
for(int i=((pos&1)?pos+1:pos+2);i<=n;i+=2)swap(a[p[i]],a[p[i+1]]);
if(pos&1)swap(a[p[pos-2]],a[p[pos]]),swap(a[p[pos-1]],a[p[pos]]);
else swap(a[p[pos-1]],a[p[pos+1]]),swap(a[p[pos-1]],a[p[pos]]);
for(int i=1;i<=n;i++)printf("%lld ",a[i]);puts("");
}
else{
if(k>1)puts("-1");
else{
for(int i=1;i<=n;i+=2)swap(a[p[i]],a[p[i+1]]);
for(int i=1;i<=n;i++)printf("%lld ",a[i]);puts("");
}
}
}
E. Disrupting Communications
点边容斥,设 为子树 的答案,,记录前后缀积。换根求出所有 为根和跨过边 的答案,维护 lca 求路径和。
G. Expanding Array
只有 能被取到,去重计数。
I. Good Partitions
对于 的位置,要求 整除 ,单点修,求全局 gcd。
K. Magical Set
每个 ,代价为 的质因数数。连边最大费用最大流。
The 3rd Universal Cup. Stage 16: Nanjing
B. Birthday Gift
反转奇数位,变为相临的 可以消掉,等价于 ,将 填到少的一边。
C. Topology
设 表示 除去 子树, 的拓扑序排名为 。预处理挖去子树 , 的兄弟及其子树的拓扑序方案数 。。前缀和转移。
F. Subway
表示 在第 条线第 个站(编号为 )的最短路,dij,线路上的转移容易。当松弛 时, 越小, 越大,在每个 维护凸包。对于可以更新的 , ,更新 最小的 。每个 的 按 排序,双指针维护 处没被松弛的最小 ,在凸包上二分 时的最小值。
int n,k;
int a[maxn],b[maxn];
vector<pii> e[maxn];
vector<int> dis[maxn];
vector<bool> vis[maxn];
vector<pii> pos[maxn];
int p[maxn];
vector<pii> st[maxn];
bool chk(pii u,pii v,pii w){
return (__int128)(w.se-u.se)*(u.fi-v.fi)<=(__int128)(v.se-u.se)*(u.fi-w.fi);
}
priority_queue<pair<int,pii>> q;
int calc(pii l,int x){return x*l.fi+l.se;}
int fd(int u,int x){
int l=0,r=st[u].size()-1,res=0;
while(l<r){
int mid=l+r>>1;
if(calc(st[u][mid],x)<=calc(st[u][mid+1],x))r=mid;
else l=mid+1;
}
return calc(st[u][l],x);
}
void work(){
n=read();k=read();
for(int i=1;i<=k;i++)a[i]=read();
for(int i=1;i<=k;i++)b[i]=read();
for(int i=1;i<=k;i++){
int len=read();
e[i].resize(len),vis[i].resize(len),dis[i].resize(len);
for(int j=0;j<len-1;j++)e[i][j]={read(),read()};
e[i][len-1].fi=read();
for(int j=0;j<len;j++){
pos[e[i][j].fi].pb({i,j});
dis[i][j]=inf;
}
}
for(int i=1;i<=n;i++)sort(pos[i].begin(),pos[i].end(),[&](pii u,pii v){return a[u.fi]<a[v.fi];});
for(int i=0;i<pos[1].size();i++){
dis[pos[1][i].fi][pos[1][i].se]=0;
q.push({0,{pos[1][i].fi,pos[1][i].se}});
}
while(!q.empty()){
int i=q.top().se.fi,j=q.top().se.se;q.pop();
if(vis[i][j])continue;vis[i][j]=1;
if(j+1<e[i].size()){
if(dis[i][j+1]>dis[i][j]+e[i][j].se){
dis[i][j+1]=dis[i][j]+e[i][j].se;
q.push({-dis[i][j+1],{i,j+1}});
}
}
int u=e[i][j].fi;
if(!st[u].size()||b[i]<st[u].back().fi){
while(st[u].size()>1&&chk(st[u][st[u].size()-2],st[u].back(),{b[i],dis[i][j]}))st[u].pop_back();
st[u].pb({b[i],dis[i][j]});
}
while(p[u]<pos[u].size()&&vis[pos[u][p[u]].fi][pos[u][p[u]].se])p[u]++;
if(p[u]<pos[u].size()){
int v=fd(u,a[pos[u][p[u]].fi]);
if(dis[pos[u][p[u]].fi][pos[u][p[u]].se]>v){
dis[pos[u][p[u]].fi][pos[u][p[u]].se]=v;
q.push({-dis[pos[u][p[u]].fi][pos[u][p[u]].se],{pos[u][p[u]].fi,pos[u][p[u]].se}});
}
}
}
for(int i=2;i<=n;i++){
int res=inf;
for(auto[id,j]:pos[i])res=min(res,dis[id][j]);
printf("%lld ",res);
}
}
I. Bingo
等于每行每列最大值的最小值。Min-Max 容斥。。枚举 有 行 列,有 个格子。设选 个格子的最大值为 ,,差卷积。
J. Social Media
对每个非朋友 求出与 个朋友有边,要么是最大的两个 之和,要么是有边的 的 。
K. Strips
黑点和左右端点将序列分为若干子问题。只有无法覆盖下一个红点时放纸条,如果超过右端点在往回调整。
M. Ordainer of Inexorable Judgment
原点到每个点为圆心半径为 的园求切线,取最大最小的夹角。因为有跨过 X 轴正方向的情况,所以是求与当前边界的夹角是否为正。
int n;
db x,y,d,t,ans;
pii a[maxn];
int sgn(db x){
if(x>eps)return 1;
if(x<-eps)return -1;
return 0;
}
pii rotate(pii u,db c){
return {u.fi*cosl(c)-u.se*sinl(c),u.fi*sinl(c)+u.se*cosl(c)};
}
int cross(pii u,pii v){return u.fi*v.se-u.se*v.fi;}
void work(){
n=read();x=read(),y=read(),d=read(),t=read();
for(int i=1;i<=n;i++)a[i]={read(),read()};
pii l={0,0},r={0,0};
for(int i=1;i<=n;i++){
db v=atan2l(d,sqrtl(a[i].fi*a[i].fi+a[i].se*a[i].se-d*d));
pii ll=rotate(a[i],-v),rr=rotate(a[i],v);
if(i==1)l=ll,r=rr;
else{
if(sgn(cross(ll,l))>=0)l=ll;
if(sgn(cross(rr,r))<=0)r=rr;
}
}
db a=atan2l(y,x),al=atan2l(l.se,l.fi),ar=atan2l(r.se,r.fi);
al-=a,ar-=a;
if(sgn(al)<0)al+=2*pi;
if(sgn(ar)<0)ar+=2*pi;
a=ar-al;
if(sgn(a)<0)a+=2*pi;
while(sgn(t-2*pi)>=0){
t-=2*pi;
ans+=a;
}
if(sgn(ar-al)>=0){
if(t>al)ans+=min(t,ar)-al;
}
else{
ans+=min(t,ar);
if(t>al)ans+=t-al;
}
printf("%.12Lf\n",ans);
}
The 3rd Universal Cup. Stage 17: Jinan
B. The Magician
记每种花色几张,搜索。
C. The Empress
构造 POP i GOTO i+1;PUSH i GOTO i
使 ,POP i GOTO i+1;PUSH i GOTO 1
使 。
D. The Emperor
设从栈顶为 进入指令 出来时在 ,长度为 。记搜。如果 处 POP a
结束,否则进入 PUSH b GOTO x
,搜 ,再搜 ,。如果搜索次数过大则无解。
int n,tim;
int op[maxn],to1[maxn],to2[maxn],v1[maxn],v2[maxn];
char s[maxn];
bool vis[maxn][maxn];
pii dp[maxn][maxn];
pii sovle(int x,int a){
if(dp[x][a].fi)return dp[x][a];
++tim;if(tim>=2*n*n)return {0,0};
if(!a&&op[x])return dp[x][a]={1,0};
if(v1[x]==a)return {1,to1[x]};
pii res=sovle(to2[x],v2[x]);
if(res.se==0){return dp[x][a]={res.fi+1,0};}
int len=res.fi;res=sovle(res.se,a);
return dp[x][a]={(res.fi+len+1)%mod,res.se};
}
void work(){
n=read();
for(int i=1;i<=n;i++){
scanf("%s",s+1);
if(s[1]=='H')op[i]=1;
else{
v1[i]=read();scanf("%s",s+1);
scanf("%s",s+1);int l=strlen(s+1);
for(int j=1;j<l;j++)to1[i]=to1[i]*10+s[j]-'0';
}
scanf("%s",s+1);v2[i]=read();
scanf("%s",s+1);to2[i]=read();
}
pii res=sovle(1,0);
printf("%lld\n",tim>=2*n*n?-1:res.fi);
}
E. The Chariot
分讨,python。
t=int(input())
while t>0:
t-=1
s=input().split();
a=int(s[0])
b=int(s[1])
c=int(s[2])
x=int(s[3])
y=int(s[4])
d=int(s[5])
res=(d+x-1)//x
ans=a*res
res=d//x;
if res!=0:
ans=min(ans,a*res+b*max(0,min(d-res*x,y))+c*max(0,d-res*x-y));
if d>=x+y:
res=d%(x+y);
ans=min(ans,(a+b*y)*(d//(x+y))+min(a+b*max(res-x,0),res*c))
ans=min(ans,a+b*y+(d-x-y)*c)
else:
ans=min(ans,a+b*max(d-x,0))
if d>=x:
u=d%x
v=d//x
if v*y>=u:
ans=min(ans,a*v+b*u)
k=(d+x+y-1)//(x+y)
if d-k*x>0:
ans=min(ans,a*k+b*(d-k*x))
print(ans)
F. The Hermit
减去每个数被删除的次数。 被删除时比 小的部分为上一个数的倍数,比 大的部分都是 的倍数。 前只能选 个,枚举倍数转移。
I. The Hanged Man
选出一个偶度数点,一个点连向的叶子两两匹配。否则无解。
J. Temperance
从小往大删,删 小的不影响 大的点,所以初始状态下 的删掉。
L. The Tower
对每个人维护动态开点线段树。加的时候从代表空节点的线段树上分裂一个前缀,删的时候和代表空节点的线段树合并。提前把点开到询问的位置,询问时从对应的线段树节点跳线段树父亲至根,记录每个根对应哪个人。