【前言】
很久没有时间整理题解了,补题和打游戏的时间居多(doge)
这场其实主要F出题人数据有锅,花太多时间了(赛后重测是一血),然后后面G想歪了爆搜剪枝没过,I的回滚莫队队友前一天写了结果今天写不出来。
不过还行,rk38,校2/9

A.Away from College

【题目】

nnn个点mmm条边的仙人掌,有qqq个询问形如(ci,di,li)(c_i,d_i,l_i)(ci​,di​,li​),表示询问一个点xix_ixi​,满足它在cic_ici​到lil_ili​的最短路上,且在cic_ici​到did_idi​的最短路上,有多个答案,优先输出离cic_ici​最远的,有多个再输出编号最小的

n,q≤105,m≤32(n−1)n,q\leq 10^5,m\leq \frac 3 2(n-1)n,q≤105,m≤23​(n−1)

【思路】

首先把圆方树建出来,然后分别考虑求 xix_ixi​ 和求 disidis_idisi​。

求 xix_ixi​ 的话首先把 LCA(ci,di)LCA(c_i, d_i)LCA(ci​,di​),$ LCA(c_i, l_i)$, LCA(di,li)LCA(d_i, l_i)LCA(di​,li​) 都求出来,然后根据这三个点的位置关系进行分类讨论,这里就先介绍其中的一种情况:如果$ LCA(d_i, l_i)$ 在 cic_ici​ 的子树中,此时如果$ LCA(d_i, l_i)$ 是原始点那么 xi=LCA(di,li)x_i = LCA(d_i, l_i)xi​=LCA(di​,li​),否则可以把问题规约成 ci,di,lic_i, d_i, l_ici​,di​,li​ 在同一个环中的情况,这个比较容易可以讨论出来。别的情况也是类似的处理。

求 disidis_idisi​ 的话,可以把每个真实点的权值标成这个点走到这个点双对应的根节点的步数,然后倍增即可。注意特殊处理一下最短路经过 LCA(ci,xi)LCA(c_i, x_i)LCA(ci​,xi​) 所在的点双的长度。

【参考代码】

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
int yh(){int ret=0;bool f=0;char c=getchar();while(!isdigit(c)){if(c==EOF)return -1;if(c=='-')f=1;c=getchar();}while(isdigit(c))ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();return f?-ret:ret;
}
const int maxn=3e5+5;
struct edge{int to,nxt,w;
}e[maxn*4];
int h[maxn],nh[maxn];
int tot=1;
void addedge(int x,int y){e[++tot]=(edge){y,h[x],1};h[x]=tot;
}
void addedge2(int x,int y,int w){e[++tot]=(edge){y,nh[x],w};nh[x]=tot;
}
int dfn[maxn],low[maxn],sta[maxn],top_=0,dfs_time;
int d[maxn],cir[maxn];
int cc=0;
int n,m,q;
void tarjan(int x,int pre){dfn[x]=low[x]=++dfs_time;sta[++top_]=x;for(int i=h[x];i;i=e[i].nxt){int y=e[i].to;if(!dfn[y]){d[y]=d[x]+1;tarjan(y,i);low[x]=min(low[x],low[y]);if(low[y]>dfn[x]){addedge2(x,y,1); top_--;}else if(low[y]==dfn[x]){int o=(++cc)+n;cir[o]=d[sta[top_]]-d[x]+1;addedge2(x,o,0);for(int u=0;u!=y;){u=sta[top_--];addedge2(o,u,min(d[u]-d[x],cir[o]-(d[u]-d[x])));}}}else if(i!=(pre^1)){low[x]=min(low[x],dfn[y]);}}
}
int fa[maxn],son[maxn],siz[maxn],dep[maxn],dis[maxn];
int top[maxn],L[maxn],R[maxn],dtime;
void dfs1(int x){siz[x]=1;for(int i=nh[x];i;i=e[i].nxt)if(e[i].to!=fa[x]){int y=e[i].to;fa[y]=x;dep[y]=dep[x]+1;dis[y]=dis[x]+e[i].w;dfs1(y);siz[x]+=siz[y];if(siz[y]>siz[son[x]]) son[x]=y;}
}
void dfs2(int x,int t){top[x]=t;L[x]=++dtime;if(son[x]) dfs2(son[x],t);for(int i=nh[x];i;i=e[i].nxt)if(e[i].to!=fa[x]&&e[i].to!=son[x]){dfs2(e[i].to,e[i].to);}R[x]=dtime;
}
int lca(int x,int y){while(top[x]!=top[y]){if(dep[top[x]]>dep[top[y]]) x=fa[top[x]];else y=fa[top[y]];}return dep[x]<dep[y]?x:y;
}
bool is(int a,int x){return L[a]<=L[x]&&R[a]>=R[x];
}
int child(int a,int x){if(is(son[a],x)) return son[a];while(fa[top[x]]!=a){x=fa[top[x]];}return top[x];
}
int dst(int x,int y){int l=lca(x,y);if(l<=n) return dis[x]+dis[y]-(dis[l]<<1);else{int x1=child(l,x),y1=child(l,y);int delta=abs(d[x1]-d[y1]);return dis[x]-dis[x1]+dis[y]-dis[y1]+min(delta,cir[l]-delta);}
}
pii check(int c,int a,int b,int x){int D=dst(x,c);return (D+dst(x,a)==dst(c,a)&&D+dst(x,b)==dst(c,b))? mkp(D,x):mkp(0,c);
}
int main(){// freopen("my.in","r",stdin);n=yh(),m=yh(),q=yh();rep(i,1,m){int x=yh(),y=yh();addedge(x,y);addedge(y,x);}tarjan(1,0);dfs1(1);dfs2(1,1);rep(i,1,q){int c=yh(),a=yh(),b=yh();int l=lca(c,a)^lca(c,b)^lca(a,b);if(l<=n){cout<<l<<" "<<dst(l,c)<<hvie;continue;}pii ans(0,c);if(fa[l])ans=max(ans,check(c,a,b,fa[l]));if(is(l,c)) ans=max(ans,check(c,a,b,child(l,c)));if(is(l,b)) ans=max(ans,check(c,a,b,child(l,b)));if(is(l,a)) ans=max(ans,check(c,a,b,child(l,a)));cout<<ans.se<<" "<<ans.fi<<hvie;}return 0;
}

B. Boxes

【题目】

有nnn个盒子,每个里面的球是黑白的概率都是12\frac 1 221​。现在知道一个球是黑还是白分别需要花费wiw_iwi​,知道一共有多少个黑多少个白需要花费CCC,问知道所有的期望最小花费。

n≤105n\leq 10^5n≤105

【思路】

首先CCC最多只用一次,而且可以在开始用,所以有两种策略。

  • 全部打开,花费∑wi\sum w_i∑wi​
  • 将wiw_iwi​升序排序,先花费CCC,剩下就是相当于一个随机01序列依次打开,开到一个后缀全是同色的为止,那么期望代价就是:C+∑wi(1−12n−i)C+\sum w_i(1-\frac 1 {2^{n-i}})C+∑wi​(1−2n−i1​)

【参考代码】

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;const int maxn=3e5+5;
int n,m;
db w[maxn],s[maxn];
int main(){cin>>n;double c;cin>>c;rep(i,1,n){cin>>w[i];}sort(w+1,w+1+n);rep(i,1,n) s[i]=s[i-1]+w[i];db ans=c,pw=0.5;dwn(i,n-1,0){ans+=pw*s[i];// cout<<s[i]<<" "<<pw<<" "<<ans<<hvie;pw*=0.5;}cout<<fixed<<setprecision(12)<<min(ans,s[n])<<hvie;return 0;
}

C. Cheating and Stealing

【题目】

两个人打乒乓球,打了nnn个小分,∀k∈[1,n]\forall k\in[1,n]∀k∈[1,n],求出当一局得kkk分获胜,且满足“赢两分”的限制时,第一个人赢的局数fkf_kfk​

n≤106n\leq 10^6n≤106

【思路】

首先,当一局得分是kkk的时候,最多会有O(nk)O(\frac n k)O(kn​)局,那么事实上总的局数是O(nlog⁡n)O(n\log n)O(nlogn)的,也就是我们只要能O(1)O(1)O(1)处理每一局就能解决这个问题。

我们首先考虑一个人得分是kkk这个问题,我们可以通过二分来解决这个问题,但这样会多一个log⁡\loglog(事实上比赛的时候我写的二分,$2\log 过去了)。一种更优秀的处理方式是预处理出过去了)。一种更优秀的处理方式是预处理出过去了)。一种更优秀的处理方式是预处理出posw[i]和和和posl[i]分别表示第分别表示第分别表示第i个个个w/l$的位置,那这样我们就可以快速找到对应位置。

接下来要考虑”赢两分“的限制,不难发现这个限制被打破一定是一堆wlwlwlwlwlwl交错出现以后有两个连续一样的字符。那么我们对于每个位置,处理出gig_igi​表示这个位置以后打破平局的位置,即可。

然后就是一些细节处理了,复杂度O(nlog⁡n)O(n\log n)O(nlogn)

【参考代码】

赛时$2\log $

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
#define ri register int
using namespace std;typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<int,ll> pil;
const int N=1e6+10,mod=998244353;int n;
int suml[N],sumw[N],sum[N],rig[N],f[N];
char s[N];int read()
{int ret=0,f=1;char c=getchar();while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}while(isdigit(c)) ret=ret*10+(c^48),c=getchar();return f?ret:-ret;
}int upm(int x){return x>=mod?x-mod:(x<0?x+mod:x);}
void up(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
void up(ll &x,ll y){x=(x+y>=mod?x+y-mod:x+y);}
int mul(int x,int y){return 1ll*x*y%mod;}int main()
{n=read();scanf("%s",s+1);for(int i=1;i<=n;++i) {suml[i]=suml[i-1]+(s[i]=='L');sumw[i]=sumw[i-1]+(s[i]=='W');sum[i]=sum[i-1]+(s[i]=='W'?1:-1);}rig[n]=rig[n+1]=n+1;for(int i=n-1;i>=1;--i){if(s[i]==s[i+1]) rig[i]=i+1;else rig[i]=rig[i+2];}//for(int i=1;i<=n;++i) printf("%d ",rig[i]);//puts("");for(int k=1;k<=n;++k){for(int l=1;l<=n;){int L=l,R=n,res=n+1;while(L<=R){int mid=(L+R)>>1;if(suml[mid]-suml[l-1]>=k || sumw[mid]-sumw[l-1]>=k) res=mid,R=mid-1;else L=mid+1;}if(res>n) break;//[l,res],r\in [res,n]if(abs(sum[res]-sum[l-1])>=2){f[k]+=(sum[res]-sum[l-1]>=2?1:0);l=res+1;continue;}int t=sum[res]-sum[l-1];if(!t){if(res+1<=n && rig[res+1]<=n){f[k]+=(sum[rig[res+1]]-sum[l-1]>=0?1:0);l=rig[res+1]+1;}else break;}else{if(res+1>n) break;if(t==1 && s[res+1]=='W') f[k]++,l=res+2;else if(t==-1 && s[res+1]=='L') l=res+2;else if(rig[res]<=n){f[k]+=(sum[rig[res]]-sum[l-1]>=2?1:0);l=rig[res]+1;}else break;}}}int ans=0;for(int i=1,pw=1;i<=n;++i) {up(ans,mul(f[i],pw));pw=mul(pw,n+1);}printf("%d\n",(ans%mod+mod)%mod);return 0;
}

随便找的1log⁡1\log1log

#include<iostream>
#include<algorithm>
#include<functional>
#include<vector>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
const int inf = 1e9;
const int max_n = 1e6 + 1000;
int prew[max_n], prel[max_n];
int id1[max_n],id2[max_n];
int tiebreak[2][max_n];
char s[max_n];
int n;
inline ll solve(int k)
{int ans = 0;int L = 1;while (L <= n){int R1 = inf, R2 = inf;int r1;if (prew[L - 1] + k > n)r1=inf;else r1 = id1[prew[L - 1] + k];if (r1==0)r1=inf;if (r1 <= n && r1-L+1-k<=k+2){if (r1-L+1-k<=k-2)R1=r1;else if (r1-L+1-k==k-1)R1 = tiebreak[0][r1+1];else if (r1-L+1-k==k){R1 = tiebreak[0][r1+1];if (R1!=inf)R1 = tiebreak[0][R1+1];}else if (r1-L+1-k==k+1){R1 = tiebreak[0][r1+1];if (R1!=inf){R1 = tiebreak[0][R1+1];if (R1!=inf)R1=tiebreak[0][R1+1];}}}if (prel[L - 1] + k > n)r1=inf;else r1 = id2[prel[L - 1]+k];if (r1==0)r1=inf;if (r1 <= n && r1-L+1-k<=k+2){if (r1-L+1-k<=k-2)R2=r1;else if (r1-L+1-k==k-1)R2 = tiebreak[1][r1+1];else if (r1-L+1-k==k){R2 = tiebreak[1][r1+1];if (R2!=inf)R2 = tiebreak[1][R2+1];}else if (r1-L+1-k==k+1){R2 = tiebreak[1][r1+1];if (R2!=inf){R2 = tiebreak[1][R2+1];if (R2!=inf)R2=tiebreak[1][R2+1];}}}L = min(R1, R2) + 1;if (R1 < R2 && R1 <= n)++ans;}return ans;
}
int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n;for (int i = 1;i <= n;++i)cin >> s[i];int tot1=0,tot2=0;for (int i = 1;i <= n;++i){prew[i] = prew[i - 1] + (s[i] == 'W');prel[i] = prel[i - 1] + (s[i] == 'L');if (s[i]=='W')id1[++tot1]=i;else id2[++tot2]=i;}tiebreak[0][n+1]=tiebreak[1][n+1]=inf;if (s[n]=='W')tiebreak[0][n]=n,tiebreak[1][n]=inf;else tiebreak[0][n]=inf,tiebreak[1][n]=n;for (int i=n-1;i>=1;--i){if (s[i]=='W'){tiebreak[0][i]=i;if (tiebreak[1][i+1]==inf)tiebreak[1][i]=inf;else tiebreak[1][i]=tiebreak[1][tiebreak[1][i+1]+1];}else if (s[i]=='L'){tiebreak[1][i]=i;if (tiebreak[0][i+1]==inf)tiebreak[0][i]=inf;else tiebreak[0][i]=tiebreak[0][tiebreak[0][i+1]+1];}}ll ans = 0, PW = 1;for (int i = 1;i <= n;++i){int Fi;Fi = solve(i);ans = (ans + Fi * PW % mod) % mod;PW = PW * (n + 1) % mod;}cout << ans << endl;
}

D. Double Strings

【题目】

给定两个字符串A,BA,BA,B,问有多少中方式,在AAA和BBB中各选一个长度相同的子序列,满足在AAA中选的字典序小于在BBB中选的。(字符位置不同即为不同)

∣A∣,∣B∣≤5000|A|,|B|\leq 5000∣A∣,∣B∣≤5000

【思路】

考虑我们怎么构造这个字典序小于的情况,必然是一段连续一样的+一个小于的字符+一段任意的

我们考虑枚举这个小于的字符,然后求出前后的情况总数。

对于前面连续一段一样的,我们可以设f[i][j]f[i][j]f[i][j]表示AAA到第iii位,BBB到第jjj位的相同子序列个数,转移有点类似二维前缀和。

对于后面一段任意的,设AAA中剩下nnn个字符可选,BBB中剩下mmm个字符可选,那答案就是∑i=0min⁡(n,m)(ni)(mi)\sum_{i=0}^{\min(n,m)}\binom{n}{i}\binom{m}{i}∑i=0min(n,m)​(in​)(im​),事实上经过推导可以发现这个东西是(n+mn)\binom{n+m}{n}(nn+m​)

那么总的复杂度O(len2)O(len^2)O(len2)

【参考代码】

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
#define ri register int
using namespace std;typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<int,ll> pil;
const int N=5005,mod=1e9+7;int n,m;
int f[N][N],fac[N<<1],ifac[N<<1];
char s[N],t[N];int read()
{int ret=0,f=1;char c=getchar();while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}while(isdigit(c)) ret=ret*10+(c^48),c=getchar();return f?ret:-ret;
}int upm(int x){return x>=mod?x-mod:(x<0?x+mod:x);}
void up(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
void up(ll &x,ll y){x=(x+y>=mod?x+y-mod:x+y);}
int mul(int x,int y){return 1ll*x*y%mod;}
int qpow(int x,int y)
{int ret=1;for(;y;y>>=1,x=mul(x,x))if(y&1) ret=mul(ret,x);return ret;
}
int C(int n,int m)
{if(n<m) return 0;if(n==m || !m) return 1;return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}int main()
{scanf("%s%s",s+1,t+1);n=strlen(s+1);m=strlen(t+1);fac[0]=ifac[0]=1;for(int i=1;i<N*2;++i) fac[i]=mul(fac[i-1],i),ifac[i]=qpow(fac[i],mod-2);for(int i=1;i<=n;++i){for(int j=1;j<=m;++j){if(s[i]==t[j]) f[i][j]=upm(f[i-1][j]+f[i][j-1]+1);else f[i][j]=upm(f[i-1][j]+f[i][j-1]-f[i-1][j-1]);//printf("%d %d %d\n",i,j,f[i][j]);}}ll ans=0;for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){if(s[i]<t[j]){up(ans,1ll*upm(f[i-1][j-1]+1)*C(n-i+m-j,m-j)%mod);//printf("%d %d %d %d\n",i,j,f[i-1][j-1]+1,C(n-i+m-j,m-j));}}printf("%lld\n",(ans%mod+mod)%mod);return 0;
}

E. Eert Esiwtib

【题目】

给定一颗nnn个点的带权有根树,每条边有一个位运算符号。qqq次询问给定ddd和xxx,表示将编号iii的节点权值变为i⋅d+aii\cdot d+a_ii⋅d+ai​后,子树xxx中,所有节点到xxx的路径权值经过边上位运算操作后的值的某种位运算和(或和,与和,异或和)。

n,q≤105,ai≤1018,d≤100n,q\leq 10^5,a_i\leq 10^{18},d\leq 100n,q≤105,ai​≤1018,d≤100

【思路】

首先这个ddd只有100,暗示我们实际上可以对每个ddd做树DP

然后设f[u][0/1/2]f[u][0/1/2]f[u][0/1/2]表示uuu 到以uuu为根的子树中所有点的路径的权值的 或和/与和/异或和。转移的话也是分情况讨论,考虑 u−>vu->vu−>v 这条边,以边权是“|”符号的情况为例。

  • (wu∣v1)∣(wu∣v2)∣...=wu∣(v1∣v2∣...)(w_u|v_1) | (w_u|v_2) | ... = w_u | (v_1 | v_2 | ...)(wu​∣v1​)∣(wu​∣v2​)∣...=wu​∣(v1​∣v2​∣...),故 f[u][0]∣=wu∣f[v][0]f[u][0] |= w_u | f[v][0]f[u][0]∣=wu​∣f[v][0],同理有 f[u][1]&=wu∣f[v][1]f[u][1] \&= w_u | f[v][1]f[u][1]&=wu​∣f[v][1]

  • KaTeX parse error: Expected '}', got '^' at position 15: (w_u|v1)\text{^̲} (wu|v2) \text…,这里得分情况讨论。首先 wuw_uwu​ 或进来不会影响 ∼wu\sim w_u∼wu​ 的那些位,所以首先有 KaTeX parse error: Expected '}', got '^' at position 15: f[u][2] \text{^̲}= \sim w_u \& …,然后考虑 wuw_uwu​ 的贡献,如果 uuu 为根的子树大小是奇数,那么wuw_uwu​ 相当于被异或了奇数次,贡献则就是 wuw_uwu​,否则如果子树大小是偶数,那么 wuw_uwu​ 就被异或了偶数次,贡献是 0。综上,有$ f[u][2] \text{^}= (Size[u] & 1 ? w_u : 0) | (\sim w_u & f[v][2])$。

边权是剩下两种符号的转移也是用类似的思路去推导,总之就是考虑奇偶性之类的,这里就不过多赘述了。

复杂度O(nd)O(nd)O(nd)

【参考代码】

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define pb push_back
#define hvie '\n'
using namespace std;
typedef long long ll;
ll yh(){ll ret=0;bool f=0;char c=getchar();while(!isdigit(c)){if(c==EOF)return -1;if(c=='-')f=1;c=getchar();}while(isdigit(c))ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();return f?-ret:ret;
}
const int maxn=1e5+5;
int n,q;
vector<pair<int,int>>adj[maxn];
ll a[maxn];
ll And[105][maxn],Or[105][maxn],Xor[105][maxn];
int sz[maxn];
void dfs(int x,ll *And,ll *Or,ll *Xor){sz[x]=1;And[x]=(1ll<<62)-1;for(auto &[y,op]:adj[x]){dfs(y,And,Or,Xor);sz[x]+=sz[y];ll o=Or[y]|a[y];ll d=And[y]&a[y];ll e=Xor[y]^a[y];switch(op){case 0:{Or[x]|=(o|a[x]);And[x]&=(d|a[x]);if(sz[y]&1) Xor[x]^=a[x];Xor[x]^=(~a[x])&e;break;}case 1:{Or[x]|=o&a[x];And[x]&=d&a[x];Xor[x]^=e&a[x];break;}case 2:{Or[x]|=(o&(~a[x]))|((~d)&a[x]);And[x]&=((~o)&a[x])|(d&(~a[x]));Xor[x]^=(sz[y]&1)?e^a[x]:e;break;}}}
}
int main(){n=yh(),q=yh();rep(i,1,n) a[i]=yh();rep(i,2,n){int f=yh(),s=yh();adj[f].pb({i,s});}rep(d,0,100){dfs(1,And[d],Or[d],Xor[d]);rep(i,1,n) a[i]+=i;}rep(i,1,q){int d=yh(),u=yh();cout<<Or[d][u]<<" "<<And[d][u]<<" "<<Xor[d][u]<<hvie;}return 0;
}

F. Finding Points

【题目】

有二维平面上有nnn个点AiA_iAi​逆十字排布,形成一个凸包,现在要在凸包内求一个点PPP,使得∠AiPAi+1\angle A_iPA_{i+1}∠Ai​PAi+1​的最小值最大。

4≤n≤100,∣xi∣,∣yi∣≤100004\leq n\leq 100,|x_i|,|y_i|\leq 100004≤n≤100,∣xi​∣,∣yi​∣≤10000

【思路】

考虑固定xpx_pxp​,其实我们可以发现这个角的函数关于yyy是单峰的,那么固定ypy_pyp​也是一样。

于是我们三分套三分即可。

复杂度O(nlog⁡2W)O(n\log ^2W)O(nlog2W)

出题人给了一个更复杂的做法:

首先二分答案 xxx,然后对于凸包上的每条边 (Ai,Aj)(A_i, A_j)(Ai​,Aj​),可以知道 PPP 的合法范围一定在经过这两个点的某个圆的一个弓形上,具体可以用圆周角的那一套理论去推导,然后问题就变成求 nnn 个弓形是否有公共点了,这题的话因为n≥4n\geq 4n≥4,不会出现 nnn 个圆只在凸包外有交点的情况,所以就看 nnn 个圆是否有交。但是,除了考虑圆的交,还得考虑和凸包本身是否相交,所以还得写一个圆的交然后再跟凸包求交。

(出题人说这个三分套三分不靠谱,实际上靠谱的很,比赛的时候数据锅了,PPP在凸包外,赛后重测我们居然是一血233)

【参考代码】

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
int yh(){int ret=0;bool f=0;char c=getchar();while(!isdigit(c)){if(c==EOF)return -1;if(c=='-')f=1;c=getchar();}while(isdigit(c))ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();return f?-ret:ret;
}
const int maxn=3e5+5;
const db eps=1e-12;
int n;
struct poi{double x,y;poi(){}poi(double x,double y):x(x),y(y){}
}p[maxn];
poi operator-(const poi&A,const poi&B){return poi(A.x-B.x,A.y-B.y);
}
db dot(poi a,poi b){return a.x*b.x+a.y*b.y;
}
db mo(poi a){return sqrt(dot(a,a));}
double ang(poi a,poi b){return acos(dot(a,b)/mo(a)/mo(b));
}
db crs(poi a,poi b){return a.x*b.y-a.y*b.x;
}
bool check(poi G){rep(i,1,n){if(crs(p[i]-G,p[i%n+1]-G)<0) return 0;}return 1;
}
db calc(poi G){db ag=1e10;if(!check(G)) return -1e10;rep(i,1,n){ag=min(ag,ang(p[i]-G,p[i%n+1]-G));}return ag;
}
db xmax,ymax;
db xmin,ymin;db calc1(db x){db ly=ymin,ry=ymax;// cout<<x<<" : ";while(ly+eps<ry){// cout<<ly<<" "<<ry<<hvie;db lmid=ly*2.0/3+ry/3.0;db rmid=ly/3.0+ry*2.0/3;if(calc(poi(x,lmid))>calc(poi(x,rmid))) ry=rmid;else ly=lmid;}return calc(poi(x,ly));
}
int main(){n=yh();poi G;xmax=-1e10,ymax=-1e10;xmin=1e10,ymin=1e10;rep(i,1,n){p[i].x=yh();p[i].y=yh();G.x+=p[i].x;G.y+=p[i].y;xmin=min(xmin,p[i].x);ymin=min(ymin,p[i].y);xmax=max(xmax,p[i].x);ymax=max(ymax,p[i].y);}G.x/=n;G.y/=n;double pi=acos(-1);db lx=xmin,rx=xmax;while(lx+eps<rx){db lmid=lx*2.0/3+rx/3.0;db rmid=lx/3.0+rx*2.0/3;if(calc1(lmid)>calc1(rmid)) rx=rmid;else lx=lmid;}cout<<fixed<<setprecision(12)<<calc1(lx)*180./pi<<hvie;return 0;
}

G. Greater Interger, Better LCM

【题目】

给定三个数a,b,ca,b,ca,b,c,求x,yx,yx,y满足lcm(a+x,b+y)=c\text{lcm} (a+x,b+y)=clcm(a+x,b+y)=c,且x+yx+yx+y最小。ccc以∏i=1npiqi\prod_{i=1}^np_i^{q_i}∏i=1n​piqi​​形式给出,其中pip_ipi​是素数。

n≤18,pi≤100,∑qi≤18,a,b,c≤1032n\leq 18,p_i\leq 100,\sum q_i\leq 18,a,b,c\leq 10^{32}n≤18,pi​≤100,∑qi​≤18,a,b,c≤1032

【思路】

这个数据范围小的一,我们考虑乱搞。

首先它给的这个ccc形式就很有意思,摆明了让我们枚举这个最大的qiq_iqi​分配到哪个数上,于是问题就变成了先分配这个qiq_iqi​。我们设f[s]f[s]f[s]表示分配给a+xa+xa+x的最大qiq_iqi​状态为sss,那么接下来就要求出这个状态下加入其他一些质因子的满足乘积≥a\geq a≥a的最小乘积(有点绕),这个状态不是很多,因为质因子不多,而且一个质因子用的次数也不多。然后再做一个传递闭包就行。另一边同理。

最后合并一下就行了。

复杂度O(n⋅2n+搜索)O(n\cdot 2^n+搜索)O(n⋅2n+搜索)

【参考代码】

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef double db;
typedef __int128_t Int;
Int yh(){Int ret=0;bool f=0;char c=getchar();while(!isdigit(c)){if(c==EOF)return -1;if(c=='-')f=1;c=getchar();}while(isdigit(c))ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();return f?-ret:ret;
}
int n;
Int inf=1;
vector<pii> c;
char stk[1000];int top_;
void print(Int x){if(x==0) {puts("0");return;}top_=0;while(x) stk[++top_]=x%10+'0',x/=10;dwn(i,top_,1) cout<<stk[i];cout<<hvie;
}
Int a,b;
Int f[1<<20],g[1<<20];
void dfs(Int *f,Int tar,Int x,int id,int S){if(x>f[S]) return;if(id==n){if(x>=tar)f[S]=min(f[S],x);return;}for(int cnt=0,D=1;cnt<=c[id].se;cnt++,D*=c[id].fi){dfs(f,tar,x*D,id+1,S|((cnt==c[id].se)*(1<<id)));}
}int main(){n=yh();rep(i,1,31) inf*=10;rep(i,1,n){int x=yh(),y=yh();c.pb({x,y});}a=yh();b=yh();int mask=(1<<n)-1;rep(i,0,mask) f[i]=g[i]=inf;dfs(f,a,1,0,0);dfs(g,b,1,0,0);dwn(s,mask,0)rep(i,0,n-1){if(s>>i&1)g[s^(1<<i)]=min(g[s^(1<<i)],g[s]);}Int ans=inf;rep(i,0,mask){ans=min(ans,f[i]+g[mask^i]);}ans-=a+b;print(ans);return 0;
}

H. Holding Two

【题意】

给定n,mn,mn,m,求一个01矩阵AAA满足没有任意三个位置a,b,ca,b,ca,b,c,满足Aa=Ab=AcA_a=A_b=A_cAa​=Ab​=Ac​且ax−bx=bx−cx,ay−by=by−cya_x-b_x=b_x-c_x,a_y-b_y=b_y-c_yax​−bx​=bx​−cx​,ay​−by​=by​−cy​

n,m≤1000n,m\leq 1000n,m≤1000

【思路】

令A[i][j]=(i+(j/2))%2A[i][j]=(i+(j/2))\%2A[i][j]=(i+(j/2))%2即可(从0开始)。

【参考代码】

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
int yh(){int ret=0;bool f=0;char c=getchar();while(!isdigit(c)){if(c==EOF)return -1;if(c=='-')f=1;c=getchar();}while(isdigit(c))ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();return f?-ret:ret;
}
const int maxn=3e5+5;
int n,m;
int main(){n=yh(),m=yh();rep(i,1,n){if(i&1){rep(j,1,m){cout<<((j-1)/2&1);}}else{rep(j,1,m){cout<<((((j-1)/2)&1)^1);}}puts("");}return 0;
}

I. Interval Queries

【题目】

给定一个长度为nnn序列AiA_iAi​,有qqq个询问给出(l,r,k)(l,r,k)(l,r,k),计算∑i=0k−1f({Al−i,...,Al,Al+1,...,Ar,...,Ari})\sum_{i=0}^{k-1}f(\{A_{l-i},...,A_l,A_l+1,...,A_r,...,A_{r_i}\})∑i=0k−1​f({Al−i​,...,Al​,Al​+1,...,Ar​,...,Ari​​}),其中fff表示最长连续值域段的长度。

n,q≤105,Ai≤n,∑k≤107n,q\leq 10^5,A_i\leq n,\sum k\leq 10^7n,q≤105,Ai​≤n,∑k≤107

【思路】

回滚莫队+链表即可。

回滚的一个实现方式:先把所有询问按照 lK\frac l KKl​ 分组,然后分配每组询问,即按rrr从小到大排序,并找到最大的lll(记为 mxlmxlmxl),如果有某个询问的 r<mxlr < mxlr<mxl,直接暴力处理这个询问,否则就可以维护一个 [mxl,R][mxl, R][mxl,R] 的区间,每次向右拓展 RRR,处理某个询问的时候就把左端点从 mxlmxlmxl 拓展到询问的 lll,算下当前答案,再把左端点退回 mxlmxlmxl,这期间只有加入和撤销操作。

这个题莫队套上的操作就是维护双向链表以及最长的链表的长度,所以加点的时候可以先把当前答案和所加的点存下来再维护答案,这样就可以很方便地进行撤销了。

复杂度O(nn+k)O(n\sqrt n +k)O(nn​+k)

【参考代码】

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
#define ri register int
using namespace std;typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<int,ll> pil;
const int N=1e5+10,mod=998244353;int n,m,B,lim;
int a[N],pw[N],ans[N];int read()
{int ret=0,f=1;char c=getchar();while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}while(isdigit(c)) ret=ret*10+(c^48),c=getchar();return f?ret:-ret;
}
int upm(int x){return x>=mod?x-mod:x;}
void up(int &x,int y){x=upm(x+y);}
int mul(int x,int y){return 1ll*x*y%mod;}struct Tquery
{int l,r,k,id;Tquery(int l=0,int r=0,int k=0,int id=0):l(l),r(r),k(k),id(id){}bool operator < (const Tquery&rhs)const{return r<rhs.r; }
};
vector<Tquery>vec[330];struct node
{int tans,x,l,r;node(int tans,int x,int l,int r):tans(tans),x(x),l(l),r(r){}
};
stack<node>st;int maxans;
int L[N],R[N],cnt[N];void add(int x)
{if(cnt[x]++) return;st.push(node(maxans,x,L[x],R[x]));int l=L[x],r=R[x];if(l>=1) R[l]=r;if(r<=n) L[r]=l;//printf("%d %d %d %d\n",x,l,r,maxans);maxans=max(maxans,r-l-1);
}
void del(int x)
{if(--cnt[x]) return;node t=st.top();st.pop();L[x]=t.l;R[x]=t.r;if(t.l>=1) R[t.l]=x;if(t.r<=n) L[t.r]=x;maxans=t.tans;
}void solve(Tquery x)
{//printf("!!!%d\n",maxans);ans[x.id]=maxans;for(int i=1;i<x.k;++i){add(a[x.r+i]);add(a[x.l-i]);up(ans[x.id],mul(maxans,pw[i]));}for(int i=x.k-1;i>=1;--i){del(a[x.l-i]);del(a[x.r+i]);}
}void init()
{for(int i=1;i<=n;++i) L[i]=i-1,R[i]=i+1,cnt[i]=0;maxans=0;
}int main()
{n=read();m=read();lim=330;pw[0]=1;for(int i=1;i<=n;++i) a[i]=read(),pw[i]=mul(pw[i-1],n+1);for(int i=1;i<=m;++i){int l=read(),r=read(),k=read();if(r-l+1<=lim) vec[0].pb(Tquery(l,r,k,i));else vec[l/lim+1].pb(Tquery(l,r,k,i));}for(auto v:vec[0]){init();for(int i=v.l;i<=v.r;++i) add(a[i]);solve(v);for(int i=v.r;i>=v.l;--i) del(a[i]);}for(int bl=1,S=n/lim+1;bl<=S;++bl){init();int tmp=bl*lim,nowl=tmp,nowr=nowl-1;sort(vec[bl].begin(),vec[bl].end());for(auto i:vec[bl]){for(;nowr<i.r;) add(a[++nowr]);for(;nowl>i.l;) add(a[--nowl]);solve(i);for(;nowl<tmp;) del(a[nowl++]);}}for(int i=1;i<=m;++i) printf("%d\n",(ans[i]%mod+mod)%mod);return 0;
}

J. Jewels

【题目】

nnn个点,第iii个点用(xi,yi,zi,vi)(x_i,y_i,z_i,v_i)(xi​,yi​,zi​,vi​)表示,现在要以某个顺序标记所有点,第ttt个标记的点花费xi2+yi2+(zi+t×vi)2x_i^2+y_i^2+(z_i+t\times v_i)^2xi2​+yi2​+(zi​+t×vi​)2,求最小花费。

n≤300,0≤xi,yi,zi,vi≤1000n\leq 300,0\leq x_i,y_i,z_i,v_i\leq 1000n≤300,0≤xi​,yi​,zi​,vi​≤1000

【思路】

一个点可以拆成nnn个时间,然后每个时间只能对应一个点,就是最小权匹配了。

【参考代码】

#include <bits/stdc++.h>using namespace std;const int MAXN = 505;int n;
namespace KM {const int MAXN = 505;
const long long INF = 1e18;int link[MAXN], pre[MAXN], mat[MAXN];
long long mp[MAXN][MAXN], lx[MAXN], ly[MAXN], slack[MAXN];
bool visx[MAXN], visy[MAXN];inline void aug(int s) {while (s) {int t = mat[pre[s]];link[s] = pre[s];mat[link[s]] = s;s = t;}return;
}void bfs(int s) {long long delta;fill(slack + 1, slack + 1 + n, INF);fill(visx + 1, visx + 1 + n, 0);fill(visy + 1, visy + 1 + n, 0);queue<int> q;while (!q.empty()) q.pop();q.push(s);while (true) {while (!q.empty()) {int fr = q.front();q.pop();visx[fr] = 1;for (int i = 1; i <= n; ++i) {if (visy[i]) continue;delta = lx[fr] + ly[i] - mp[fr][i];if (!delta) {visy[i] = 1;pre[i] = fr;if (!link[i]) return aug(i);q.push(link[i]);} else if (slack[i] > delta) slack[i] = delta, pre[i] = fr;}}delta = INF;for (int i = 1; i <= n; ++i) if (!visy[i]) delta = min(delta, slack[i]);for (int i = 1; i <= n; ++i) {if (visx[i]) lx[i] -= delta;if (visy[i]) ly[i] += delta;else slack[i] -= delta;}for (int i = 1; i <= n; ++i) {if (!visy[i] && !slack[i]) {visy[i] = 1;if (!link[i]) return aug(i);q.push(link[i]);}}}return;
}long long KM() {long long ans = 0;fill(lx + 1, lx + 1 + n, -INF);for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) lx[i] = max(lx[i], mp[i][j]);for (int i = 1; i <= n; ++i) bfs(i);for (int i = 1; i <= n; ++i) ans += lx[i] + ly[i];return ans;
}
}struct Point {int x, y, z, v;
} a[MAXN];long long sqr(long long x) {return x * x;
}long long cal(int x, int t) {return sqr(a[x].x) + sqr(a[x].y) + sqr(a[x].z + a[x].v * t);
}signed main() {scanf("%d", &n);for (int i = 1; i <= n; ++i) {scanf("%d%d%d%d", &a[i].x, &a[i].y, &a[i].z, &a[i].v);}for (int i = 1; i <= n; ++i) {for (int j = 1; j <= n; ++j) {KM::mp[i][j] = -cal(j, i - 1);}}printf("%lld\n", -KM::KM());return 0;
}

K. King of Range

【题目】

给定一个长度为nnn的序列aia_iai​,有mmm个询问,给出一个常数kkk,问有多少个区间[l,r][l,r][l,r]满足区间所有数字极差大于kkk。

n≤105,m≤200,ai,k≤109n\leq 10^5,m\leq 200,a_i,k\leq 10^9n≤105,m≤200,ai​,k≤109

【思路】

一个无脑的做法是,首先RMQ预处理出区间最小值最大值。对于每个询问,双指针扫一下就行了。

复杂度O(nlog⁡n+nm)O(n\log n+nm)O(nlogn+nm)

【参考代码】

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
#define ri register int
using namespace std;typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<int,ll> pil;
const int N=1e5+10,mod1=1e9+7,mod2=1e9+9;int n,m;
int a[N],mi[19][N],mx[19][N];
int Log[N];int read()
{int ret=0,f=1;char c=getchar();while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}while(isdigit(c)) ret=ret*10+(c^48),c=getchar();return f?ret:-ret;
}int getmin(int l,int r)
{int k=Log[r-l+1];return min(mi[k][l],mi[k][r-(1<<k)+1]);
}
int getmax(int l,int r)
{int k=Log[r-l+1];return max(mx[k][l],mx[k][r-(1<<k)+1]);
}int main()
{n=read();m=read();for(int i=2;i<N;++i) Log[i]=Log[i>>1]+1;for(int i=1;i<=n;++i) a[i]=read(),mi[0][i]=mx[0][i]=a[i];for(int j=1;j<19;++j) {for(int i=1;i<=n;++i)if(i+(1<<j)-1<=n) {mi[j][i]=min(mi[j-1][i],mi[j-1][i+(1<<(j-1))]);mx[j][i]=max(mx[j-1][i],mx[j-1][i+(1<<(j-1))]);}   }while(m--){int k=read();int r=1;ll ans=0;for(int i=1;i<=n;++i){int tmin=getmin(i,r),tmax=getmax(i,r);while(tmax-tmin<=k && r<=n) {++r;if(r>n) break;tmin=getmin(i,r),tmax=getmax(i,r);}if(r>n) break;ans+=(n-r+1);}printf("%lld\n",ans);}return 0;
}

【多校训练】2021牛客多校5相关推荐

  1. LCS(2021牛客多校4)

    LCS(2021牛客多校4) 题意: 让你构造三个字符串s1,s2,s3,长度均为n,要求LCS(s1,s2)=a,LCS(s2,s3)=b,LCS(s1,s3)=c 题解: 先考虑三个串互相LCS为 ...

  2. 【2021牛客多校2】F-Girlfriend 计算几何

    2021牛客多校2-F F-Girlfriend 题目大意 给出四个点 A , B , C , D A, B, C, D A,B,C,D 另有两点 P 1 , P 2 P_1, P_2 P1​,P2​ ...

  3. K-Stack 2021牛客多校2

    链接:https://ac.nowcoder.com/acm/contest/11253/K 来源:牛客网 题目描述 ZYT had a magic permutation a1,a2,⋯ ,an a ...

  4. 2021牛客多校第八场补题 D-OR

    链接:https://ac.nowcoder.com/acm/contest/11259/D 来源:牛客网 题目描述 There are two sequences of length n−1n-1n ...

  5. 2021牛客多校第五场补题

    B-Boxes 链接:https://ac.nowcoder.com/acm/contest/11256/B 来源:牛客网 题目描述 There're nn_{}n​ boxes in front o ...

  6. 【多校训练】2021牛客多校第一场

    [前言] 组队训练的第一场比赛,感觉这场出题十分阴间,后面几个乱搞题根本不会.jpg 赛时只过了5题,rk123,学校参加5/8. A. Alice and Bob [题意] 两人博弈,每次一个人从一 ...

  7. 【多校训练】2021牛客多校第二场

    [前言] 这是打的第二场,rk39,但是AB这两个比较简单的题都没做emm,大概还是磨合的不够.然后感觉对于阈值类的东西还不是很敏感,应该看到不太好做就直接去想这种阈值的.校内3/9(然后就开启了常年 ...

  8. 2021牛客多校9 - Cells(推公式+NTT)

    题目链接:点击查看 题目大意:初始时给出 nnn 个点,分别为 {(0,a0),(0,a1),⋯,(0,an)}\{(0,a_0),(0,a_1),\cdots,(0,a_n)\}{(0,a0​),( ...

  9. (2021牛客多校一)A.Alice and Bob(博弈)

    样例输入: 5 2 3 3 5 5 7 7 5 7 7 Bob Alice Bob Bob Alice 题意:有两堆石子,石子数目分别为n和m,Alice和Bob轮流进行以下操作: 从一堆石子中取出k ...

最新文章

  1. 线性判别分析(Linear Discriminant Analysis)(一)
  2. 大规模数据中心如何重塑整个IT
  3. MFC—对话框程序—模式对话框与非模式对话框
  4. java生成json字符串,威力加强版
  5. flask 重定向(redirect)和会话(session)
  6. Semaphore示例
  7. BZOJ.2428.[HAOI2006]均分数据(随机化贪心/模拟退火)
  8. 京东一面:高并发下,如何保证分布式唯一全局 ID 生成?
  9. sublime安装插件详细教程
  10. Milk-Tea解析工具(DJ音乐解析)
  11. for linux pdf转mobi_pdftotext —— Linux/Unix中将PDF文件转化为Text文本格式的利器
  12. python之并发编程-进程之间的通信
  13. 【python 淘宝爬虫】python 淘宝店铺名称,旺旺,销售量 抓取
  14. m4a转如何快速转换为wav格式呢
  15. android胎心监测设计,胎心监测
  16. 京东到家话费券系统NIO实战
  17. pyquery 使用说明(支持python 3)
  18. LVGL (7) 显示对接
  19. python测试框架untest_python自动化测试框架unittest
  20. 惊闻同事噩耗,思绪联翩

热门文章

  1. PKPM工程造价2008中文版​​​​​​​
  2. 技美知识学习3300:TESS and GS
  3. SDL2.0--绘制文字
  4. 4-思科防火墙:访问控制列表:基本ACL
  5. 显示器色彩精度测试软件,色彩测试篇:色彩还原准确度测试_SANC E7 PRO_液晶显示器评测-中关村在线...
  6. 在线flash文本的抓取方法
  7. 便携式微型计算机和笔记本的区别,笔记本和超极本及上网本 细谈三者区别
  8. eCharts给图标添加边框
  9. 关于使用MethodHandle在子类中调用祖父类重写方法的探究
  10. TRIZ创新方法——ARIZ算法