牛客OI周赛13-提高组

正题

      这次比赛打得不错,想出了两道题,期望230,实际180。

      T1

      其实挺好玩的,考虑到^0,|0,&1之后数值不会改变,^1翻转,|1变成1,&0变成0。

      把第一个数位可以换成|?,然后规定开始为0。

      考虑到一个操作序列,有用的位置一定是最后一个“变成”和最后一些翻转,想到可以枚举最后一个变成是哪个位置,该位置前面所有数位随便取,后面的翻转个数为奇数或偶数,其他不变,那么这个就是一个组合数奇/偶数位之和,除了0的情况比较特殊,其他的肯定都是2^{n-1},二项式定理即可证明。

      结果比赛的时候手残写了一个错的对拍,交了一个错的观察程序。(还能水到30分

#include<bits/stdc++.h>
using namespace std;
 
const int N=10000010;
char s[N];
long long ci[N];
int n;
const long long mod=1e9+7;
 
int main(){
    scanf("%d",&n);
    scanf("%s",s+1);s[0]='|';
    ci[0]=1;for(int i=1;i<=n;i++) ci[i]=ci[i-1]*2%mod;
    long long ans=0,t=0;
    for(int i=n;i>=0;i--) if(s[i]=='^') t++;else{
        if(t==0 && s[i]=='&') continue;
        else if(t==0) ans+=ci[i];
        else ans+=ci[i]*ci[t-1]%mod;
    }
    if(t) ans+=ci[t-1];
    printf("%lld",ans%mod);
}

      T2

      首先要会做一种情况的,直接离散化以后用一个树状数组去维护就可以了,两种情况就分开讨论就可以了,互相必定没有影响。离散化懒得可以map。

#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
using namespace std;
 
const int N=100010;
int mmax1[N<<1],mmax2[N<<1],n,ans,t,tmp;
int x[N],y[N];
map<int,int> mp;
map<int,int>::iterator it;
 
void change(int*a,int x,int c){
    while(x<=t){
        a[x]=max(a[x],c);
        x+=lowbit(x);
    }
}
 
int get_max(int*a,int x){
    int mmax=0;
    while(x){
        mmax=max(mmax,a[x]);
        x-=lowbit(x);
    }
    return mmax;
}
 
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d %d",&x[i],&y[i]),mp[x[i]]=1,mp[y[i]]=1;
    for(it=mp.begin();it!=mp.end();it++) (*it).second=++t;
    for(int i=1;i<=n;i++) {
        x[i]=mp[x[i]],y[i]=mp[y[i]];
        if(x[i]==y[i]) continue;
        if(x[i]<y[i]){
            tmp=get_max(mmax1,t-x[i])+1;
            ans=max(ans,tmp);
            change(mmax1,t-y[i]+1,tmp);
        }
        else{
            tmp=get_max(mmax2,x[i]-1)+1;
            ans=max(ans,tmp);
            change(mmax2,y[i],tmp);
        }
    }
    printf("%d",ans);
}

      T3

      “当他给你输出6位小数而不是模的时候,你就要知道这个题就是来搞你心态的” ---来自Freopen巨神

      考虑到往下70层的时候影响十分小,所以我们只用考虑前70层。

      期望与概率之间的转化就是

      \\E(x)=\sum_{i=1}P(x>=i) \\E(x)=\sum_{i=1}1-P(x<i)

      P(x>=i)表示最大层数在i以下的概率,补集转化就可以得到第二条式子。

      这个东西可以用一个f[x][i]来维护第i个点往下走不了i层的概率。

      转移的时候先考虑去掉前面的影响,在考虑父亲-儿子这条边选或者不选带来的概率。

      选的时候,下面必须满足走不到i-1层,并且要满足从其他子树走走不到第i层。

      不选的时候,下面没有要求,要满足从其他子树走走不到第i层。

      具体写法可以看代码。

      那么式子就很好写了,时间复杂度就是O(sz\ n)

#include<bits/stdc++.h>
using namespace std;

const int N=500010,sz=70;
int T,q,tot,fa[N];
double f[N][sz];

int main(){
	scanf("%d %d",&T,&q);tot=1;
	fill(f[1]+1,f[1]+sz,1);f[1][0]=0;
	int type,x;
	while(q--){
		scanf("%d %d",&type,&x);
		if(type==1){
			fa[++tot]=x;
			fill(f[tot]+1,f[tot]+sz,1);f[tot][0]=0;
			double now,last=1;
			for(int i=1,op=tot;x!=0 && i<sz;i++,x=fa[op=x]) now=(f[x][i]+1)/2,f[x][i]*=(f[op][i-1]+1)/2/last,last=now;
		}
		else{
			double ans=0;
			for(int i=1;i<sz;i++) ans+=1-f[x][i];
			printf("%.10lf\n",ans);
		}
	}
}

猜你喜欢

转载自blog.csdn.net/Deep_Kevin/article/details/103462803