第二次周赛(Codeforces Beta Round #84 (Div. 1 Only))

转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove

第二次周赛,由#84的div1以及#83的div2的AB组成

#83 div2 A  直接枚举,秒数+1


#83 div2 B 两个n位的二进制数相乘,结果最多为2*n位(二进制),根据这个,排序后判断


#84 div1 A 直接枚举,从少到多,枚举7的个数,7的个数越多,位数就越少,数字就越小。

                  结果显然是4在前,7在后


#84 div B

这题 有点蛋疼,我的做法是,首先预处理出所有的lucky number

然后开始枚举a[i],表示左端点要保存a[i]能取,但是a[i-1]不取,那么右端点就要保证b[i+k-1]能取,但是b[i+k]不能取

分别以A为左端点扫描一遍,B为左端点扫描一遍。

但是这样会忽视掉一种情况,就是K=1的情况,有可能左端点和右端点重合,如[4,4]刚好有一个lucky number。

所以要遍历一遍, 去重

int vl,vr,pl,pr,k;
vector<int>a;
void bfs()
{
    queue<int>que;
    que.push(0);
    while(!que.empty())
    {
        int u=que.front();

        que.pop(); if(u>1e8) continue;
        int tmp=u*10+4;
        if(tmp>1e9||tmp<0) ;else {que.push(tmp);a.pb(tmp);}
        tmp=u*10+7;
        if(tmp>1e9||tmp<0) ;else {que.push(tmp);a.pb(tmp);}
    }
}
int main()
{
    a.pb(0);
    bfs();
   // for(int i=1;i<a.size();i++) printf("%d\n",a[i]);
    a.pb(1000000005);
   // freopen("1.in","r",stdin);
    while(scanf("%d%d%d%d%d",&vl,&vr,&pl,&pr,&k)!=EOF)
    {
        LL tot=(LL)(vr-vl+1)*(pr-pl+1);
        LL tmp=0,cnt=0;
        if(k==1)
        {
            for(int i=1;i<a.size()-1;i++) if(a[i]>=vl&&a[i]<=vr&&a[i]>=pl&&a[i]<=pr) cnt++;
        }
        for(int i=1;i<a.size()-k+1-1;i++)
        {
            if(a[i]<vl) continue;
            if(a[i-1]>=vr) continue;
            if(a[i+k-1]>pr) continue;
            if(a[i+k]<=pl) continue;
            tmp+=(LL)(min(vr,a[i])-max(a[i-1]+1,vl)+1)*(min(pr,a[i+k]-1)-max(pl,a[i+k-1])+1);
        }
        for(int i=1;i<a.size()-k+1-1;i++)
        {
            if(a[i]<pl) continue;
            if(a[i-1]>=pr) continue;
            if(a[i+k-1]>vr) continue;
            if(a[i+k]<=vl) continue;
            tmp+=(LL)(min(pr,a[i])-max(a[i-1]+1,pl)+1)*(min(vr,a[i+k]-1)-max(vl,a[i+k-1])+1);
        }
        printf("%.10f\n",(tmp-cnt)*1.0/tot);
    }
    return 0;
}

#83 div1 C

一个树DP,处理从一个点出发,到达多少个点的路径中会出现Lucky nubmer。

果然tree_dp不是我的料,写得很渣。

以1为根,处理出所有子树的size

然后向下更新,表示从当前结点,往下遍历,会有多少个点的路径上出现lucky number。显然,如果当前路径便是lucky number,便直接加上size,否则加上孩子的down。

然后向上更新,表示从当前结点,往上遍历,会有多少个点的路径上出现lucky number。显然如果从父亲到当前节点的路径为Lucky number,便直接加上上面的所有节点数目,n-size[v]。否则便要算上父节点的向上更新以下,当前节点的所有兄弟结点(便是父亲的向下更新,减去当前的向下更新)

一般的一次DP就解决了,哎太弱了

struct Edge
{
    int v,next;
    int f;
}e[N];
int cnt=0,start[N],sz[N];
int down[N],up[N];
set<int>a;
int n;
void add(int u,int v,int f)
{
    e[cnt].v=v;e[cnt].f=f;e[cnt].next=start[u];
    start[u]=cnt++;
    e[cnt].v=u;e[cnt].f=f;e[cnt].next=start[v];
    start[v]=cnt++;
}
void bfs()
{
    queue<int>que;
    que.push(0);
    while(!que.empty())
    {
        int u=que.front();
        que.pop();
        int tmp=u*10+4;
        if(tmp>1e9||tmp<0) ;else {que.push(tmp);a.insert(tmp);}
        tmp=u*10+7;
        if(tmp>1e9||tmp<0) ;else {que.push(tmp);a.insert(tmp);}
    }
}
void fuck1(int u,int pre)
{
    sz[u]=1;
    for(int i=start[u];i!=-1;i=e[i].next)
    {
        int v=e[i].v;
        if(v==pre) continue;
        fuck1(v,u);
        sz[u]+=sz[v];
    }
}
void fuck2(int u,int pre)
{
    for(int i=start[u];i!=-1;i=e[i].next)
    {
        int f=e[i].f,v=e[i].v;
        if(v==pre) continue;
        fuck2(v,u);
        if(f) down[u]+=sz[v];
        else down[u]+=down[v];
    }
}
void fuck3(int u,int pre)
{
    for(int i=start[u];i!=-1;i=e[i].next)
    {
        int v=e[i].v,f=e[i].f;
        if(v==pre) continue;
        if(f) up[v]+=n-sz[v];
        else {up[v]+=up[u];
        up[v]+=down[u]-down[v];
        }
        fuck3(v,u);
    }
}
int main()
{
    bfs();
    mem(start,-1);
    scanf("%d",&n);
    mem(up,0);mem(down,0);
    for(int i=1;i<n;i++)
    {
        int f,u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        if(a.find(w)==a.end()) f=0;
        else f=1;
        add(u,v,f);
    }
    fuck1(1,0);
    fuck2(1,0);
    fuck3(1,0);
    LL ans=0;
    for(int i=1;i<=n;i++)
    {
        ans+=(LL)(up[i]+down[i]-1)*(up[i]+down[i]);
    }
    printf("%I64d\n",ans);
    return 0;
}

#83 div1 D 
是给出一个序列,给出交换规则(至少有一个数是lucky number)
给出的交换次数是2*n,输出任意解
找出一个lucky number用来和其它的交换
遍历所有位置,如果当前位置不是选中的lucky number的位置,则考虑交换,则Lucky number到了另一个位置,再考虑排序之后在这个位置的是哪个数,这个数的当前位置。
再将Lucky number换过去,这样从第一个位置开始遍历,最多两次交换可以把一个数归位。
最后我再把lucky number换到应该属于他自己的位置
另外其中有种数据,我的代码没有问题,结果CF的SPJ判我WA,便把if(i!=pos)省略了,结果就过了, 这不科学啊
其实这样也是对的,次数看上去是2*n+1次,但是绝不会出现这种情况,仔细想想就知道了。

int check(int n)
{
    while(n)
    {
        int t=n%10;
        if(t!=4&&t!=7) return 0;
        n/=10;
    }
    return 1;
}
int n,a[N];
int b[N];    //表示应该排在第i个位置的数现在的位置
int c[N];    //表示第i个数最终放在哪
vector<pair<int,int> >ans;
bool cmp(int i,int j)
{
    return a[i]<a[j];
}
void slove(int &x,int y)
{
    if(x==y) return;
    if(!check(a[x])&&!check(a[y])) puts("error");
    swap(a[x],a[y]);
    ans.pb(mp(x,y));
    swap(c[x],c[y]);
    b[c[x]]=x;
    b[c[y]]=y;
    x=y;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i],b[i]=i;
    sort(b+1,b+1+n,cmp);
    int pos=-1;
    for(int i=1;i<=n;i++) c[b[i]]=i;
    for(int i=1;i<=n;i++) if(check(a[i])) {pos=i;break;}
    if(pos==-1)
    {
        int flag=1;
        for(int i=1;i<n;i++)if(a[i]>a[i+1]) flag=0;
        puts(flag?"0":"-1");
        return 0;
    }
    for(int i=1;i<=n;i++)
    {
       // if(i!=pos)
        {
            slove(pos,i);
            slove(pos,b[i]);
        }
    }
    slove(pos,b[pos]);
    cout<<ans.size()<<endl;
    for(int i=0;i<ans.size();i++) cout<<ans[i].first<<" "<<ans[i].second<<endl;
    return 0;
}







你可能感兴趣的:(第二次周赛(Codeforces Beta Round #84 (Div. 1 Only)))