写在前面
\(Day1\),这场考试难度适中,我觉得海星
\(T1\) 有理树\((SBT)\)
\(Idea\)
\(Stern-Brocot\ Tree \to SBT\)
容易发现在每一行都存在\(\displaystyle \frac{m}{n} \lt \frac{m+m'}{n+n'} \lt \frac{m'}{n'}\)
根据图还发现,当\(\displaystyle\frac{a}{b}\)小于当前节点时,向左走\((L)\);大于当前节点时,向右走\((R)\)
并且发现题目中不存在向下走的情况,去掉多余点后,发现这是一颗完全二叉树(然而并没有什么卵用
,请忽略这行话设当前节点为\(\displaystyle \frac{m}{n}\),则
当\(\displaystyle \frac{a}{b} \gt \frac{m}{n}\)时,\(a \times n>b \times m\);
当\(\displaystyle \frac{a}{b}<\frac{m}{n}\)时,\(a \times n<b \times m\);
根据这个条件进行判断
什么时候结束呢?
当\(a \times n=b \times m\)时。原因很简单
结束时\(\displaystyle \frac{m}{n} = \frac{\frac{a}{gcd(a,b)}}{\frac{b}{gcd(a,b)}}\),所以\(a \times n=b \times m=\displaystyle \frac{a \times b}{gcd(a,b)}\),(应该没错。。。吧。。。
再仔细分析一波,\(a \times b\)好像爆\(int\)了,所以要开\(long\ long\)。
\(Code\)
signed main(){ // freopen(File".in","r",stdin); // freopen(File".out","w",stdout); int a=read(),b=read(); int x=0,y=1; int m=1,n=1; int m1=1,n1=0; while(a*n!=b*m){ if(a*n<b*m){ putchar('L'); m1=m; n1=n; } else{ putchar('R'); x=m;y=n; } m=x+m1; n=y+n1;//按规则计算下一行的分子分母。 } return 0; }
\(T2\) 字符串问题\((string)\)
\(Idea\)
\(20\ pts\):搜索,预计复杂度\(O(T \ast k!)\)
\(50\ pts\):建图,跑最长路,用\(Bellman-Ford/SPFA\)判断负环,复杂度\(O(T \ast k^4)\)
\(100\ pts\):新建一个节点,向所有点连边权为0的边,或者直接让所有点距离为0开始跑最长路。复杂度\(O(T \ast k^3)\)
\(Code\)
int w[maxn][maxn]; int ans,dis[maxn]; signed main(){ // freopen(File".in","r",stdin); // freopen(File".out","w",stdout); int T=read(); while(T--){ mem(dis,0); ans=0; //初始化很重要 int k=read(); for(int i=1;i<=k;i++) for(int j=1;j<=k;j++) w[i][j]=read(); for(int l=1;l<=k;l++) for(int i=1;i<=k;i++) for(int j=1;j<=k;j++) dis[j]=max(dis[j],dis[i]+w[i][j]); bool flag=1; for(int i=1;i<=k;i++) for(int j=1;j<=k;j++) if(dis[j]<dis[i]+w[i][j]) flag=0; if(flag){ for(int i=1;i<=k;i++) ans=max(ans,dis[i]); printf("%d\n",ans); } else puts("-1"); } return 0; }
\(T3\) 序列\((sequence)\)
\(Idea\)
考场上自然是暴力做了,暴力自然不难
对于\(50\ pts\),使用\(ST\)表,复杂度\(O(n^2)\)
对于\(100\ pts\),使用二分+分治思想;
找到全局最小值,处理跨过最小值的区间,然后分裂为两块。
最小值和一个端点确定后,满足条件的另一个端点处的值是确定的,于是只需统计该权值在对应区间的出现次数。
对每个权值,记录下其出现的全部位置,然后二分就可以知道其在某个区间出现了多少次。
枚举端点时选择枚举较短的一边可以保证复杂度,因为每次被计算代表大小至少减半,于是一个位置最多计算\(\log n\)次
复杂度\(O(n \log^2 n)\)
我们有三种策略:
- \(O(n^3)\)枚举\(l,r\),再枚举\(\displaystyle \min_{l \le k \le r}a_k\)
我们的式子是\(a_l \oplus a_r \oplus a_{\min}=D\),我们相当于知三求一
已知\(a_l,a_{\min},D\),求\(a_r\);
已知\(a_r,a_{\min},D\),求\(a_l\)
以上两个式子等效
\(Code\)
//暴力,根据题意的暴力T_T int a[maxn],ans; inline int find(int l,int r){ int res=inf; for(int i=l;i<=r;i++) res=min(res,a[i]); return res; } signed main(){ // freopen(File".in","r",stdin); // freopen(File".out","w",stdout); int n=read(),D=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=n;i++) for(int j=i;j<=n;j++){ if((a[i]^a[j]^find(i,j))==D) ans++; } printf("%d",ans); return 0; } //该暴力复杂度O(n^3),预计20 pts,开O2可以有50pts。。。。
const int N=100005,M=1<<20; int T,n,D; int a[N]; int lc[N],rc[N]; int stk[N],tp; vi bac[M]; LL ans; void solve(int w,int l,int r){ if(w-l<r-w){ for(int i=l;i<=w;++i){ int v=D^a[i]^a[w]; ans+=lower_bound(bac[v].begin(),bac[v].end(),r+1)-lower_bound(bac[v].begin(),bac[v].end(),w); } }else{ for(int i=w;i<=r;++i){ int v=D^a[i]^a[w]; ans+=lower_bound(bac[v].begin(),bac[v].end(),w+1)-lower_bound(bac[v].begin(),bac[v].end(),l); } } if(lc[w])solve(lc[w],l,w-1); if(rc[w])solve(rc[w],w+1,r); } int main(){ freopen(File".in","r",stdin); freopen(File".out","w",stdout); n=read();D=read();ans=0; for(int i=0;i<N;++i)bac[i].clear(); for(int i=1;i<=n;++i){ a[i]=read();bac[a[i]].push_back(i); if(!tp||a[i]>=a[stk[tp]])stk[++tp]=i; else{ for(;tp&&a[i]<a[stk[tp]];--tp)rc[stk[tp-1]]=stk[tp]; lc[i]=stk[tp+1];stk[++tp]=i; } } for(;tp;--tp)rc[stk[tp-1]]=stk[tp]; solve(rc[0],1,n); printf("%lld\n",ans); fclose(stdin);fclose(stdout); return 0; }
\[ The \quad End \]
\[ \text{她渐渐忘了我,但是她并不晓得;遍体鳞伤的我,一天也在没爱过。-《那女孩对我说》} \]