模拟赛day2 2014 10 26
来源:互联网 发布:哪个软件可以听歌识曲 编辑:程序博客网 时间:2024/06/18 18:29
T1:
题目大意:给你一颗二叉树,再给你每个点的权值a[i],让你改变最少的点的权值,是的满足任意的p,a[lch]<a[p],a[rch]>a[p],lch代表p的左儿子及其子树,rch代表p的右儿子及其子树,权值必须时刻为整数(可以为负数或者0) n<=100000
这题是原题,但是我当时没做,还好考的时候想出来了
首先看到一棵树很烦躁,所以就把树按中序遍历搞出来,就转化成求把一个序列变成严格上升最小需要改多少个数
设f[i]代表第i个数不改,满足性质最少需要多少步,f[i]=i-1 min{f[k]-k} a[i]-a[k]>=i-k
a[i]-a[k]>=i-k 就等价于 a[i]-i>=a[k]-k
这个我当时是这样做的,先按a[i]-i排序,再在树状数组中的i位置插入f[i]-i,求最小值就OK,不过这样比较慢
其实这就是一个最长上升子序列,先按a[i]-i>=a[k]-k排序就好了 时间复杂度O(nlog2(n))
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int inf=2147483647,maxn=100011;int c[maxn][3],n,a[maxn];void init(){scanf("%d",&n);for (int i=1;i<=n;++i) scanf("%d",a+i);int a,b;for (int i=2;i<=n;++i){scanf("%d%d",&a,&b);c[a][b]=i;}}struct Tbit{int t[maxn];void clear(){memset(t,63,sizeof(t));}void ins(int x,int m){for (;x<=n;x+=x&(-x)) t[x]=min(t[x],m);}int Query(int x,int ans=inf){for (;x;x-=x&(-x)) ans=min(ans,t[x]);return ans;}}bit;int A[maxn],stack_x[maxn],stack_p[maxn];void Gets(){int cnt=0,top=0;stack_x[++top]=1;stack_p[top]=0;while (top){int x=stack_x[top],p=stack_p[top];if (!x || p>2){--top;++stack_p[top];continue;}if (p==0){stack_x[++top]=c[x][0];stack_p[top]=0;continue;}if (p==1){++cnt;A[cnt]=a[x]-cnt;++stack_p[top];continue;}if (p==2){stack_x[++top]=c[x][1];stack_p[top]=0;continue;}}}inline bool cmp(int *a,int *b){if (*a==*b) return a-A<b-A;return *a<*b;}int *p[maxn],f[maxn];void work(){Gets();A[0]=-inf;A[++n]=inf;for (int i=1;i<=n;++i) p[i]=A+i;sort(p+1,p+n+1,cmp);for (int i=1;i<=n;++i){int x=p[i]-A;f[x]=bit.Query(x)+x-1;bit.ins(x,f[x]-x);}//for (int i=1;i<=n;++i) cout<<i<<' '<<f[i]<<endl;printf("%d\n",f[n]);}int main(){init();work();return 0;}
题目大意:给你n个数a[i],现在要找到一对(l,r) 使得存在一个k l<=k<=r 对于任意的a[i]%a[k]==0 l<=i<=r n<=500000 a[i]<2^31
这题可以预处理出来一个ST表,然后就可以了,复杂度是O(nlog2(n)log2(a)) 的,但是求GCD是远小于log2(a)的,所以还是可以过的
#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int maxn=500011,maxq=25;inline int read(){char ch=getchar();int x=0;while (!isdigit(ch)) ch=getchar();for (;isdigit(ch);ch=getchar()) x=x*10+ch-'0';return x;}int n,a[maxn];void init(){n=read();for (int i=1;i<=n;++i) a[i]=read();}inline int gcd(int m,int n){int t;while (n!=0){t=m%n;m=n;n=t;}return m;}int p[maxq],f[maxq][maxn][2],quit;void prepare(){quit=(int)log2(n);for (int i=0;i<=quit;++i) p[i]=1<<i;for (int i=1;i<=n;++i) f[0][i][0]=f[0][i][1]=a[i];for (int i=1;i<=quit;++i) for (int j=n;j>=1;--j){if (j-p[i]+1<1) break;f[i][j][0]=gcd(f[i-1][j][0],f[i-1][j-p[i-1]][0]);}for (int i=1;i<=quit;++i) for (int j=1;j<=n;++j){if (j+p[i]-1>n) break;f[i][j][1]=gcd(f[i-1][j][1],f[i-1][j+p[i-1]][1]);}}int getl(int x){int tmp=x-1;for (int i=quit;i>=0;--i) if (tmp-p[i]+1>=1 && f[i][tmp][0]%a[x]==0) tmp=tmp-p[i];return tmp+1;}int getr(int x){int tmp=x+1;for (int i=quit;i>=0;--i) if (tmp+p[i]-1<=n && f[i][tmp][1]%a[x]==0) tmp=tmp+p[i];return tmp-1;}int l[maxn],r[maxn];bool vis[maxn];void work(){prepare();for (int i=1;i<=n;++i) l[i]=getl(i),r[i]=getr(i);int ans=0,sum=0;for (int i=1;i<=n;++i) if (r[i]-l[i]>ans) ans=r[i]-l[i];for (int i=1;i<=n;++i) if (r[i]-l[i]==ans) vis[l[i]]=1;for (int i=1;i<=n;++i) if (vis[i]) ++sum;printf("%d %d\n",sum,ans);for (int i=1;i<=n;++i) if (vis[i]) printf("%d ",i);}int main(){init();work();return 0;}这题还可以用各种数据结构维护,都可以在给定的时间内通过
不过这题还可以用一个神奇的O(n*a(n))的方法做,a(n) 是并查集常数,怒虐std 10倍(zzb大神发明的)
具体来说:我们先枚举k,再看其左右分别能扩展多远就行了
我们只考虑左边,如果对于一个j,有j<i且对于任意 i 有a[i]%a[k]==0 j<=i<=k 就可以把j到k合并到一个点,因为后面的点可以扩展到k,就一定可以扩展到j
合并的话,就只要在并查集上连跳边就行了
#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int maxn=500011,maxq=25;inline int read(){char ch=getchar();int x=0;while (!isdigit(ch)) ch=getchar();for (;isdigit(ch);ch=getchar()) x=x*10+ch-'0';return x;}int n,a[maxn],fa[maxn];int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}void init(){n=read();for (int i=1;i<=n;++i) a[i]=read();}int l[maxn],r[maxn];bool vis[maxn];void work(){for (int i=1;i<=n;++i) fa[i]=i;for (int i=1,j,last;i<=n;++i){for (j=i-1,last=i;j>0;j=find(j)-1)if (a[j]%a[i]==0) fa[last]=j,last=j; else break;l[i]=j+1;}//for (int i=1;i<=n;++i) cout<<i<<' '<<l[i]<<endl;//for (;;);for (int i=1;i<=n;++i) fa[i]=i;for (int i=n,j,last;i>=1;--i){for (j=i+1,last=i;j<=n;j=find(j)+1)if (a[j]%a[i]==0) fa[last]=j,last=j; else break;r[i]=j-1;}int ans=0,sum=0;for (int i=1;i<=n;++i) if (r[i]-l[i]>ans) ans=r[i]-l[i];for (int i=1;i<=n;++i) if (r[i]-l[i]==ans) vis[l[i]]=1;for (int i=1;i<=n;++i) if (vis[i]) ++sum;printf("%d %d\n",sum,ans);for (int i=1;i<=n;++i) if (vis[i]) printf("%d ",i);}int main(){init();work();return 0;}
题目大意:给你一个1到n的排列P,求有多少个Q(1到n-1的排列) 使得 swap(P[Q[1]],P[Q[1]+1]) swap(P[Q[2]],P[Q[2]+1]) .... swap(P[Q[n-1]],P[Q[n-1]+1]) 后,P满足P[i]==i,答案mod 1000000007 n<=50
这题完全没思路,就打了个O(n!) 的暴力拿了30分
正解是这样的
因为换的是排列,所以(i,i+1) 只能被换一次,要保证换完后所有在i左边的小于i,在i右边的大于i
所以可以记忆化搜索, f[l][r]代表l到r有多少种方案可以使得l到r从小到大排好
枚举一个k,先把P[k],P[k+1]换一下,如果check(l,k) && check(k+1,r) 就可以用k来更新f[l][r] check(a,b) 代表P[a]到P[b]中的数都在a到b范围内
具体来说f[l][r]+=f[l][k]*f[k+1][r]*C(r-l-1,k-l) 这个是为什么,自己仔细想想就知道了
#include<cstdio>#include<cstring>#define swap(a,b) a^=b^=a^=busing namespace std;const int maxn=53,mod=1000000007;int a[maxn],n,f[maxn][maxn],c[maxn][maxn];void init(){scanf("%d",&n);for (int i=1;i<=n;++i) scanf("%d",a+i),++a[i];}bool check(int l,int r){for (int i=l;i<=r;++i) if (a[i]<l || a[i]>r) return 0;return 1;}int dfs(int l,int r){if (l==r) return 1;if (f[l][r]) return f[l][r];for (int k=l;k<r;++k){swap(a[k],a[k+1]);if (check(l,k) && check(k+1,r)) f[l][r]=(f[l][r]+1LL*dfs(l,k)*dfs(k+1,r)%mod*c[r-l-1][k-l]%mod)%mod;swap(a[k],a[k+1]);}return f[l][r];}void prepare(){c[0][0]=1;for (int i=1;i<=n;++i) for (int j=0;j<=i;++j) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;}void work(){prepare();dfs(1,n);printf("%d\n",f[1][n]);}int main(){init();work();return 0;}
今天考的还行,能拿的分都拿到了
- 模拟赛day2 2014 10 26
- day2模拟赛总结
- CQOI2016 day2 模拟赛总结
- 2- noip模拟赛 DAY2
- contesthunter NOIP模拟赛Day2 8.10
- 山东多校联合模拟赛 Day2
- NOIP2014 Day2 模拟赛赛后总结&题解
- 省选模拟赛[HEOI2012] Day2
- 省选模拟赛[SHOI2017] Day2
- 2014.08.10 CH Round#49-Streaming#4(NOIP模拟赛Day2) 总结
- 洛谷 NOIP 模拟 DAY2
- CH Round #49 - Streaming #4 (NOIP模拟赛Day2)
- CH Round #55 - Streaming #6 (NOIP模拟赛day2)总结
- 计蒜客 2017 NOIP 提高组模拟赛(一)Day2
- 计蒜客 2017 NOIP 提高组模拟赛(二)Day2
- 2017 NOIP 提高组模拟赛(四)Day2(计蒜客)
- 补noip2014day1、noip2014模拟day2
- 【省选模拟】【HNOI2015 day2】
- 手机联网状态判断
- Win7 硬盘安装Ubuntu14.04.1 LTS
- 分娩待产包中的6件必备利器
- HDU 1009 FatMouse' Trade
- 深度学习资料整理(博客类)
- 模拟赛day2 2014 10 26
- 编写程序
- PostgreSql 常用命令
- codeforces——1A - Theatre Square
- Windows 常用消息及含义
- Java程序员的JavaScript学习笔记(9—— jQuery工具方法)
- 随想录(做自己代码的测试工程师)
- 网友超级实用待产包清单明细分享推荐
- TestFlight Beta Testing - 介绍