[3.9校内训练赛]

Posted FallDream

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[3.9校内训练赛]相关的知识,希望对你有一定的参考价值。

不要纠结为什么3.9的比赛比3.10的还迟做完,你们首先得知道这场是ditoly出的

ditoly丧病出题人啊丧病出题人啊丧病出题人啊

本机上 T1 std3.000s卡过,我的3.1s被卡了 T3 总共2s,std写的treap跑的飞快,我写的替罪羊2.1s也被卡了 

听说学校机子T1只要2s,学校机子真神奇,MBA还是不适合跑程序,低压i5伤不起啊......

分割线------------------------------------

A.一个n*m的网格图,每个格子都有一个0-9的权值,求(1,1)到(n,m)的最短路径。n,m<=5000,数据随机生成。

不会做,交了个spfa上去只有50,一个学弟写的dij过了70,真的牛逼。

题解:把每个权值大于0的点拆成很多个权值为1的点,然后bfs,遇到0的点就往外先扩展。由于数据随机生成,复杂度期望为4.5*nm

发现直接用stl的队列和手写队列差了大概0.2s,反正本机就是跑不过去,假装已经在学校机子上A了。

#include<iostream>
#include<cstdio>
#define INF 2000000000
#define MX 15000000
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-) f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0; ch=getchar();}
    return x*f;
}

int n,m,xx,yy,nowx,nowy,top=0,tail=0;
char s[5005][5005];
int d[5005][5005];
bool b[5005][5005];
short qx[MX+5],qy[MX+5];
const int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}};

void solve(int x,int y)
{
    for(int i=0;i<4;i++)
    {
        xx=x+dis[i][0];
        yy=y+dis[i][1];
        if(b[xx][yy])continue;
        b[xx][yy]=1;d[xx][yy]=d[x][y]+s[xx][yy]-0;
        if(s[xx][yy]==0)solve(xx,yy);
        else qx[++top>MX?(top-=MX):top]=xx,qy[top]=yy;
    }
}

int main()
{
    freopen("secret.in","r",stdin);
    freopen("secret.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=n;i++)
        scanf("%s",s[i]+1);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            d[i][j]=INF;
    d[1][1]=s[1][1]-0;
    qx[++top]=1;qy[top]=1;b[1][1]=1;
    for(int i=0;i<=max(n,m);i++)
        b[i][0]=b[0][i]=b[n+1][i]=b[i][m+1]=1;
    while(top!=tail&&d[n][m]==INF)
    {
        nowx=qx[++tail>MX?(tail-=MX):tail];nowy=qy[tail];
        if(s[nowx][nowy]>0) --s[nowx][nowy];
        if(s[nowx][nowy]==0) solve(nowx,nowy);
        else qx[++top>MX?(top-=MX):top]=nowx,qy[top]=nowy;
    }
    printf("%d\n",d[n][m]);
    return 0;
}

T2.k组询问,每次给定n,求∑f(i)/i  2<=i<=n,其中f(i)表示i能够分为多少种完全k次方数。 保留8位小数,差值不超过10-8判对, n<=10^36 k<=100000

std做法:用pq对10^12暴力,这时候答案0.9999998几,然后发现答案不会超过1,剩下的打表。

我的做法: 枚举次数(最多50),然后暴力枚举底数直到结果超过10^15,这时候已经0.99999997,打表最后几个数。我以为是输出答案相差不超过10-8,就输出0.99999999,结果是正确答案和你的输出答案不超过10-8,被坑了,100000组询问错了几十组,真的难受。实际上再二分一下就能过了,但我懒,也不想改了。

我的复杂度是  MAXN=10^15  log*(MAXN^0.5+MAXN^0.33+...+MAXN^0.02) log是快速幂的小log,复杂度非常科学,但就输在了打表上。

80/100,打的小表是每个次数的底数最大值,真正的打表部分处理在输入那里,需要自己二分一个区间。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXN 1000000000000000LL
#define ll long long
using namespace std;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-) f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0; ch=getchar();}
    return x*f;
}

double ad[100005];
double ts[100005];
double ans[100005];
char st[50];
const int lim[51]={0,0,31622776,100000,5623,1000,316,138,74,46,31,23,17,14,11,10,8,7,6,6,5,5,4,4,4,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2};
int t;
ll sum;

ll pow(int x,ll p)
{
    ll sum=1;
    for(ll i=x;p;p>>=1,i*=i)if(p&1) sum*=i;
    return sum;
}

struct node{
    ll x;int num;
}s[100005];

bool cmp(node x,node y){return x.x<y.x;}

int main()
{
    freopen("password.in","r",stdin);
    freopen("password.out","w",stdout);
    t=read();
    for(int i=1;i<=t;i++)
    {
        scanf("%s",st+1);int len=strlen(st+1);
        if(len>18) s[i].x=MAXN,ad[i]=0.00000003;
        else if(len>15) s[i].x=MAXN,ad[i]=0.00000002;
        else for(int j=1;st[j];j++) s[i].x=s[i].x*10+st[j]-0;
        s[i].num=i;
    }
    sort(s+1,s+t+1,cmp);
    for(int l=2;l<50;++l)
    {
        memset(ts,0,sizeof(ts));
        for(int i=2,j=1;i<=lim[l]&&j<=t;i++)
        {
            sum=pow(i,l);
            for(;s[j].x<sum&&j<=t;++j);
            if(j<=t) ts[j]+=(double)1/sum;
        }
        for(int i=1;i<=t;i++)
            ans[i]+=(ts[i]=ts[i-1]+ts[i]);
    }
    for(int i=1;i<=t;i++) ad[s[i].num]+=ans[i];
    for(int i=1;i<=t;i++) printf("%0.8lf\n",ad[i]);
    return 0;
} 

C.给定一个长度为n的数列,每个数都是1-n以内。m次询问,每次询问一个区间,再给定s和k个下标。如果区间内有一个数出现超过区间长度一半,答案是那个数,否则答案是s,然后把k个下标的位置的数字改成这次的答案。求每一次的答案和最后整个数列的答案。 n,m<=500000,∑k <=1000000

有个学长用了随机化A了,结果被某个无良出题人临时搞数据卡了40.

做法:如果只考虑一个区间求有没有出现那么多次的人,我们可以采用一个很经典的做法。先记下第一个数和此时的出现次数(一开始是1),然后一个个往后处理,如果这个数和现在几下的数不同,那么出现次数-1,否则出现次数+1。如果出现次数变为0,则把记下的数字改成现在这个数,这样一定能找到那个数。

比如3 2 4 3 3    (3,1)->(2,0)->(4,1)->(3,0)->(3,1),找到了数字3

我们发现这个操作满足区间加法,所以可以用线段树来维护,每次从区间中找到那个数字。

可是如果没有出现次数大于一半的数字怎么办?很简单,我们可以用一棵平衡树来维护每个数字的出现位置,去里面查询一下就好啦。复杂度(n+k)logn

常数大没有考虑splay,就写了替罪羊,讲道理也不慢,但是就是在本机被卡了。std写的treap跑的飞快,有空学学。

#include<iostream>
#include<cstdio>
#define MAXN 1500000
#define N 524288
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-) f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0; ch=getchar();}
    return x*f;
}

bool b[MAXN+5];
int fa[MAXN+5],size[MAXN+5],c[MAXN+5][2],nn[MAXN+5],q[MAXN+5],s[MAXN+5],col[MAXN+5];
int rt[500005],mark=0,n,m,top,cnt=0;
struct data{
    int x,num;
    data operator+(data y){if(!num)return y;if(!y.num)return *this;if(x==y.x)return(data){x,num+y.num};
                           if(num>y.num)return (data){x,num-y.num};return(data){y.x,y.num-num};}
}T[N*2+5];

void renew(int x,int ad)
{
    T[x+=N].x=ad;
    for(x>>=1;x;x>>=1) T[x]=T[x<<1]+T[x<<1|1];
}

data query(int l,int r)
{
    data sum=(data){0,0};
    for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1)sum=sum+T[l+1];
        if( r&1)sum=sum+T[r-1];
    }
    return sum;
}

void init(){for(int i=N;i;i--)T[i]=T[i<<1]+T[i<<1|1];}

void ins(int&x,int rk,int last)
{
    //cout<<"ins"<<x<<" "<<rk<<" "<<last<<endl;
    if(!x){x=++cnt;size[x]=1;nn[x]=rk;fa[x]=last;b[x]=1;col[x]=s[rk];return;}
    if(rk<=nn[x])ins(c[x][0],rk,x);else ins(c[x][1],rk,x);
    size[x]=size[c[x][0]]+size[c[x][1]]+b[x];
    if(max(size[c[x][0]],size[c[x][1]])>0.7*size[x]) mark=x;
}

void dfs(int x)
{
    if(c[x][0])dfs(c[x][0]);
    if(b[x])q[++top]=x;
    if(c[x][1])dfs(c[x][1]);
}

void build(int&x,int l,int r,int last)
{
    if(l>r)return;int mid=(l+r)>>1;x=q[mid];fa[x]=last;
    build(c[x][0],l,mid-1,x);build(c[x][1],mid+1,r,x);
    size[x]=size[c[x][0]]+size[c[x][1]]+b[x];
}

void rebuild(int x)
{
    top=mark=0;dfs(x);int y=fa[x];
    for(int i=1;i<=top;i++)fa[q[i]]=c[q[i]][0]=c[q[i]][1]=0;
    if(!y)build(rt[col[x]],1,top,0);
    else build(c[y][c[y][1]==x],1,top,y);
}

void del(int x,int k)
{
   // cout<<"del"<<x<<" "<<k<<endl;
    if(nn[x]==k&&b[x]){b[x]=0;size[x]--;return;}
    if(k<=nn[x])del(c[x][0],k);else del(c[x][1],k);
    size[x]=size[c[x][0]]+size[c[x][1]]+b[x];
}

int query2(int x,int r)
{
    //cout<<"query2"<<x<<" "<<r<<" "<<nn[x]<<endl;
    if(!x)return 0;
    if(nn[x]>r)return query2(c[x][0],r);
    else return size[c[x][0]]+b[x]+query2(c[x][1],r);
}

void solve(int l,int r,int q)
{
    data xx=query(l,r);if(xx.num&&query2(rt[xx.x],r)-query2(rt[xx.x],l-1)>=(r-l+3)/2) q=xx.x;
   // cout<<xx.x<<" "<<query2(rt[xx.x],r)-query2(rt[xx.x],l-1)<<"!!"<<endl;
    printf("%d\n",q);if(!m)return;int k=read();
    for(int j=1;j<=k;j++)
    {int x=read();if(s[x]!=q){renew(x,q);del(rt[s[x]],x);s[x]=q;ins(rt[q],x,0);if(mark)rebuild(mark);}}
}

int main()
{
   freopen("president.in","r",stdin);
   freopen("president.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=n;i++)
    {s[i]=read();T[i+N]=(data){s[i],1};ins(rt[s[i]],i,0);if(mark)rebuild(mark);}init();
    while(m--){int l=read(),r=read(),q=read();solve(l,r,q);}
    solve(1,n,-1);
    return 0;
}

 

丧病出题人丧病出题人!!!!!!!!!!!

 

以上是关于[3.9校内训练赛]的主要内容,如果未能解决你的问题,请参考以下文章

[3.16校内训练赛]

[3.3校内训练赛]

2017-4-7校内训练

两所大学中的智能车竞赛校内赛

校内第二次天梯赛总结

寒假训练计划