题解
今天做的是当年某市联考的内部资料。所以题面会更加概括的。(出题人写题解的的时候说是水题但….当年大部分都是0分)我也…..
第一题——树状数组(ftree)
【题目描述】
- T组数据,给出三段二进制
S1,S2,S3
S
1
,
S
2
,
S
3
和数字
n
n
表示一个数
- 并给出树状数组的运算方式 lowbit(S)−1 l o w b i t ( S ) − 1 来更新 S S 直至问要进行几次运算。
- 首先明确 lowbit(x)=x&(x+1) l o w b i t ( x ) = x & ( x + 1 )
- 而对 x x 的二进制的末尾全部都是, x+1 x + 1 的末尾就全部都是0了。那么在第一次 lowbit l o w b i t 操作之后,最后的数都变成了0。
- 再-1,就又在最后位1之后变成了1。
- 手动模拟下,就可以知道要多少此操作就可以了。本质操作就是除去最后连续的1之外,之前存在的所有1的个数+1
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
void fff(){
freopen("ftree.in","r",stdin);
freopen("ftree.out","w",stdout);
}
char s1[1100],s2[1100],s3[1100];
int n,T;
int main(){
fff();
scanf("%d",&T);
while (T--){
scanf("%s%s%s",s1,s2,s3);
scanf("%d",&n);
bool flag1=false,flag2=false,flag3=false;
int t1=0,t2=0,t3=0,t=0;
int sum=0;
int l=strlen(s3);
for (int i=l-1;i>=0;i--){
if(s3[i]=='0') flag3=true;
if(flag3&&s3[i]=='1') t3++;
}
sum+=t3;
l=strlen(s2);
for (int i=l-1;i>=0;i--){
if(flag3){
if(s2[i]=='1') t2++;
}else{
if(s2[i]=='0') flag2=true;
if(flag2&&s2[i]=='1') t2++;
}
if(s2[i]=='1') t++;
}
if(!flag3&&flag2)sum+=((n-1)*t+t2);
else if(flag3) sum+= t2*n;
else if(!flag2) sum=sum;
l=strlen(s1);
for (int i=l-1;i>=0;i--){
if(flag2||flag3){
if(s1[i]=='1') t1++;
}else{
if(s1[i]=='0') flag1=true;
if(flag1&&s1[i]=='1') t1++;
}
}
sum+=t1;
sum++;
printf("%d\n",sum);
}
}
第二题——运动会(meeting)
【题目描述】
- 给出一个人在 m m 人当中在项的各个排名分数,求出他总排名的期望值。
- 排名为分数小于他的分数的人数+1。
- 讲道理这种期望题对我来说就是爆零的题。
- 这是个概率题,那可以利用dp求解。
- 方程:
dp[i][j]=∑j−1k=max(j−m),k≠(j−a[i])dp[i−1][k]m−1 d p [ i ] [ j ] = ∑ k = m a x ( j − m ) , k ≠ ( j − a [ i ] ) j − 1 d p [ i − 1 ] [ k ] m − 1
( a[i] a [ i ] 是当前项目的该人的分数)
- 虽然题解给出的方程解释的不是很清楚,但大致就是:对于一个人的期望可以先求出他的这个分数的概率。
- 对于总得分为k,第 i i 个项目的排名得分为的概率就是在第 i−1 i − 1 个项目总得分为 k−j k − j 的所有概率之和除以 m−1 m − 1 (因为剩下每一个人都有相等的几率来得到这个分数)。然后一层层地乘下去。
- 转移过程可以利用 O(1) O ( 1 ) 代替 O(n) O ( n ) 由于每一个数都要除以 m−1 m − 1 那么可以将其提出,然后求出当前层的所有分数的期望值再来对下一层进行运算。
- 虽然是个很玄学的过程。但确实得到了正确答案
(真的是NOIP????)
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
void fff(){
freopen("meeting.in","r",stdin);
freopen("meeting.out","w",stdout);
}
const int MAXN=100005;
int n,m;
int a[105],s=0;
long double f[2][MAXN],sum[2][MAXN];
int main(){
fff();
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
s+=a[i];// get signal sum
}
if(m==1){
printf("%14lf\n",1.0);
return 0;
}
int t=0;//round
f[0][0]=1;//f[t][j]表示当前项目能够拿j分的期望人数
sum[0][0]=1;//sum[t][j]表示累加到当前分数的总期望人数
for (int i=1;i<=m*n;i++)
sum[0][i]=sum[0][i-1]+f[0][i];
for (int i=1;i<=n;i++){//number
for (int j=1;j<=n*m;j++){//fenshu
int l=max(j-m,0),r=j-1;//l表示..r表示上一个分数
f[t^1][j]=(sum[t][r]-(l==0?0:sum[t][l-1]))/(m-1);//下一项的得分为j的期望人数是当前项的所有期望人数的递推
if(j-a[i]>=0)//减去自己的概率,因为自己在大于这个分数的时候也会被包括到m-1的人当中
f[t^1][j]-=f[t][j-a[i]]/(m-1);//
}
for (int j=0;j<=n*m;j++)
f[t][j]=0;//滚动置零
t^=1;
sum[t][0]=f[t][0];
for (int j=1;j<=m*n;j++)
sum[t][j]=sum[t][j-1]+f[t][j];//滚动求和
}
long double ans=sum[t][s-1]*(m-1);
printf("%.14lf\n",(double)ans+1);
}
第三题——区间gcd(gcd)
【题目描述】
- 给出T组数据。每组为长度为 n n 的序列,对序列进行m此查询,给出区间,求出 [l,r] [ l , r ] 之间的 gcd g c d 的值并且求出与此区间相等的 gcd g c d 的值相等的区间个数。
- 首先我们知道,在区间当中 gcd g c d 可以有单调性,即区间越大, gcd g c d 越小。同时 gcd g c d 可以利用st倍增来进行预处理。查询的第一问来进行类似于 O(1) O ( 1 ) 的查询。效率比较高。
- 而对于第二问,考场上真的没什么思路。dasxxx大佬相处twopointer的算法进行线性查找,但无奈复杂度太高只拿了90分。标程用处了二分(what???)。枚举左端点,将所得的二分答案放到map当中。(我这个直接暴力存结果炸了内存orz)
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
#define LL long long
using namespace std;
void fff(){
freopen("gcd.in","r",stdin);
freopen("gcd.out","w",stdout);
}
const int MAXN=100002;
int T;
map<int, LL> S;
int n,m,q;
int a[MAXN];
int e[MAXN],cnt[MAXN];
int st[MAXN][20];
int gcd(int a,int b){
return (b==0)? a:gcd(b,a%b);
}
int query(int l,int r){
int y=e[r-l+1];
return gcd(st[l][y],st[r-(1<<y)+1][y]);
}
void work(int x){
int y=x;
int cnt=0;
while (y<=n){
int k=query(x,y);
int l=y,r=n,yy;
while (l<=r){
int mid=(l+r)>>1;
if(query(x,mid)==k) yy=mid,l=mid+1;
else r=mid-1;
}
S[k]+=yy-y+1;
y=yy+1;
}
}
int main(){
fff();
scanf("%d",&T);
while (T--){
scanf("%d",&n);
S.clear();
e[1]=0;
int j=0;
for (int i=2;i<=n;i++){
if(1<<(j+1)<=i) j++;
e[i]=j;
}
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
st[i][0]=a[i];
}
for (j=1;j<=19;j++){
for (int i=1;i<=n&&(i+(1<<j)-1<=n);i++){
st[i][j]=gcd(st[i][j-1],st[i+(1<<j)-(1<<(j-1))][j-1]);
}
}
for (int i=1;i<=n;i++){
work(i);
}
scanf("%d",&m);
for (int i=1;i<=m;i++){
int l,r;
scanf("%d%d",&l,&r);
int k=query(l,r);
printf("%d %lld\n",k,S[k]);
}
}
}