3.14省选模拟

3.14省选模拟

$60+60+91=211$

$T1$想出正解了,然后素数我预处理素数处理少了,还有一个变量原本是$ull->int$,$100->60$

$T2$评测机的$spj$寄了,$100->60$

$T3$写了个没有势能保护的线段树,由于当年省选数据过水$40->91$

算是这几天打的最好的一次比赛了

$T1$

简单的树同构+换根$dp$问题

#include<bits/stdc++.h>
#define ull unsigned long long
#define MAXN 200005
using namespace std;
ull hasha[MAXN],hash1[MAXN],Mid[MAXN];
int head[MAXN],nxt[MAXN],to[MAXN],Ans=MAXN,tot;
int pri[MAXN],siz[MAXN],du[MAXN],cnt;
vector<int>rd[MAXN];
bool vis[MAXN*10];
map<ull,bool>mp;
void add(int u,int v)
{
     tot++;
     to[tot]=v;
     nxt[tot]=head[u];
     head[u]=tot;
}
void Init()
{
     for(int i=2;i<=MAXN*10-5;i++)
     {
          if(!vis[i])
         {
             pri[++cnt]=i;
         }
         for(int j=1;j<=cnt&&i*pri[j]<=MAXN*10-5;j++)
         {
             vis[i*pri[j]]=1;
             if(i%pri[j]==0) break;
         }
      }
}
void dfs(int now,int fa)
{
     siz[now]=1;
     hasha[now]=1ull;
     for(int i=head[now];i;i=nxt[i])
     {
          int y=to[i];
          if(y==fa) continue;
          dfs(y,now);
          siz[now]+=siz[y];
          hasha[now]+=hasha[y]*pri[siz[y]];
     }
}
void dfs_gen(int now,int fa)
{
     int now_siz=siz[now];
     ull now_hash=hasha[now];
     for(int i=head[now];i;i=nxt[i])
     {
          int y=to[i];
         if(y==fa) continue;
         hasha[now]-=(1ull*hasha[y]*pri[siz[y]]);
         siz[now]-=siz[y];
         siz[y]+=siz[now];
         hasha[y]+=hasha[now]*pri[siz[now]];
         Mid[y]=hasha[y];
         mp[hasha[y]]=true;
         dfs_gen(y,now);
         siz[now]=now_siz;
         hasha[now]=now_hash;
     }
}
void dfs_b(int now,int fa)
{
     siz[now]=1;
     hash1[now]=1ull;
     for(int i=0;i<rd[now].size();i++)
     {
          int y=rd[now][i];
          if(y==fa) continue;
          dfs_b(y,now);
          siz[now]+=siz[y];
          hash1[now]+=hash1[y]*pri[siz[y]];
     }
}
void dfs_genb(int now,int fa)
{
     int now_siz=siz[now];
     ull now_hash=hash1[now];
     for(int i=0;i<rd[now].size();i++)
     {
          int y=rd[now][i];
          if(y==fa) continue;
          if(du[y]==1)
          {
              if(mp[hash1[now]-pri[1]])
              {
                 Ans=min(Ans,y);
              }
          }
     }
     for(int i=0;i<rd[now].size();i++)
     {
          int y=rd[now][i];
          if(y==fa) continue;
         hash1[now]-=(1ull*hash1[y]*pri[siz[y]]);
         siz[now]-=siz[y];
         siz[y]+=siz[now];
//         cout<<"y: "<<y<<" "<<hash1[y]<<" ";
         hash1[y]+=hash1[now]*pri[siz[now]];
//         cout<<hash1[y]<<endl;
         dfs_genb(y,now);
         if(du[now]==1&&now==1)
         {
             if(mp[hash1[y]-pri[1]])
             {
                 Ans=min(Ans,now);
             }
         }
         siz[now]=now_siz;
         hash1[now]=now_hash;
     }
}
int n,u,v;
int main()
{
//    freopen("a.in","r",stdin);
//    freopen("a.out","w",stdout);
    scanf("%d",&n);
    Init();
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs(1,1);
    Mid[1]=hasha[1];
    dfs_gen(1,1);
//    for(int i=1;i<=n;i++)
//    {
//        cout<<"rt: "<<i<<" "<<Mid[i]<<endl;
//    }
    n++;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        rd[u].push_back(v);
        rd[v].push_back(u);
        du[u]++;
        du[v]++;
    }
    dfs_b(1,1);
    dfs_genb(1,1);
    cout<<Ans<<endl;
}

 

$T2$

随机化,此时就是让求每个节点能到达多少叶子节点,暴力跑肯定不行,这个时候用随机化搞事情

首先对于每个节点不能存所有能到达的叶子,那么就对于能到的叶子存其中$k$个叶子,对于每个叶子随机一个权值,满足$W_i\in[1,RANDMAX]$

首先我们定义我们能取到的叶子的第$k$小记录下来

$\frac{F_k}{RANDMAX}=\frac{k}{ans}$

这个式子的意思是,你有多少叶子,那么第$k$小的叶子的期望权值也是把所有叶子平均分到所有值域上,第$k$个的位置也是对应的区间

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1000004,S=50;
int f[MAXN][S],val[MAXN][2],n,m;
double Ans[MAXN]; 
int main()
{
    srand(20050323);
    cin>>n>>m;
    for(int i=m+1;i<=n;i++)
    {
        cin>>val[i][0]>>val[i][1];
    }
    for(int t=1;t<=2;t++)
    {
        for(int i=1;i<=m;i++)
        {
            for(int j=0;j<S;j++)
            {
                f[i][j]=rand();
            }
        }
        for(int i=m+1;i<=n;i++)
        {
            for(int j=0;j<S;j++)
            {
                Ans[i]+=(f[i][j]=min(f[val[i][0]][j],f[val[i][1]][j]));
            }
        }
    
    }  
     for(int i=m+1;i<=n;i++)
    {
        cout<<(int)(RAND_MAX/Ans[i]*S*2.0-0.5)<<endl;
    }
    return 0;
}

 

$T3$

显然吉老师线段树,由于正解过于繁琐,我在考场上写了一个没有势能保护的线段树

可以获得$91pts$的好成绩

#include<bits/stdc++.h>
#define int long long
#define rs ((now<<1)|1)
#define ls (now<<1)
#define MAXN 100005
using namespace std;
struct node
{
       int l,r,Min,sum,lsum,rsum,mxsum;
}tr[MAXN<<2];
int a[MAXN],n,q;
void upd(int now)
{
     tr[now].sum=(tr[ls].sum+tr[rs].sum);
     tr[now].Min=min(tr[ls].Min,tr[rs].Min);
     tr[now].lsum=max(tr[ls].sum+tr[rs].lsum,tr[ls].lsum);
     tr[now].rsum=max(tr[rs].sum+tr[ls].rsum,tr[rs].rsum);
     tr[now].mxsum=max(max(tr[ls].mxsum,tr[rs].mxsum),tr[rs].lsum+tr[ls].rsum);
}
void build(int now,int l,int r)
{
     tr[now].l=l,tr[now].r=r;
     if(l==r)
     {
         tr[now].Min=a[l];
         tr[now].sum=a[l];
        tr[now].lsum=max(0ll,a[l]);
        tr[now].rsum=max(0ll,a[l]);
        tr[now].mxsum=max(0ll,a[l]);        
         return ;
     }
     int mid=(l+r)>>1ll;
     build(ls,l,mid);
     build(rs,mid+1,r);
     upd(now);
//     cout<<"now: "<<l<<" "<<r<<" "<<tr[now].lsum<<" "<<tr[now].rsum<<" "<<tr[now].mxsum<<endl;
}
void change(int now,int l,int r,int x)
{
     if(tr[now].Min>=x) return ;
     if(tr[now].l==tr[now].r)
     {
         tr[now].Min=x;
         tr[now].sum=x;
        tr[now].lsum=max(0ll,x);
        tr[now].rsum=max(0ll,x);
        tr[now].mxsum=max(0ll,x);
        return ;
     }
     int mid=(tr[now].l+tr[now].r)>>1ll;
     if(l<=mid) change(ls,l,r,x);
     if(r>mid)  change(rs,l,r,x);
     upd(now);
}
node query(int now,int l,int r)
{
     if(tr[now].l>=l&&tr[now].r<=r)
     {
        return tr[now];
     }
     int mid=(tr[now].l+tr[now].r)>>1ll;
     if(r<=mid) return query(ls,l,r);
     if(l>mid)  return query(rs,l,r);
     node a=query(ls,l,r);
     node b=query(rs,l,r);
     node c;
     c.sum=a.sum+b.sum;
     c.lsum=max(a.sum+b.lsum,a.lsum);
     c.rsum=max(b.sum+a.rsum,b.rsum);
     c.mxsum=max(max(a.mxsum,b.mxsum),b.lsum+a.rsum);
     return c;
}
int opt,l,r,x;
signed main()
{
//    freopen("c.in","r",stdin);
//    freopen("c.out","w",stdout);
    scanf("%lld%lld",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
    }
    build(1,1,n);
    for(int i=1;i<=q;i++)
    {
        scanf("%lld",&opt);
        if(opt==0)
        {
           scanf("%lld%lld%lld",&l,&r,&x);
           change(1,l,r,x);
        }
        else
        {
            scanf("%lld%lld",&l,&r);
            printf("%lld\n",query(1,l,r).mxsum);
        }
    }
}
/*
5 7
2 -4 6 -5 5
1 1 5
0 1 5 -4
1 1 5
0 3 4 -1
1 1 5
0 1 3 -1
1 1 5
*/

 

正解:

吉老师线段树基本操作,区间取$min/max$

这个的操作是维护最大子段和

这个东西无非是记录三个东西,$lmax,rmax,Max$

到了这大概就豁然开朗了,我们还是照样维护这几个东西,只不过你还需要维护一个这三个区间内最小值出现的个数,那么更改的时候,找到了更改值在最小值和次小值之间,就可以停止递归操作了(并不是)

主要问题是我并不知道当前节点的$pre,bed$会不会变或者当前节点的最大值会不会变,首先最大值肯定是前面的,其实最大区间的不需要记录的,改变的时候跟着改,然后最后询问的时候直接问就好了

至于我们不知道是否$pre$和$bed$是否会变,那就设个阈值就好了

写法很$nb,$貌似这题当年还是$XXF$官方数据,确实是水数据了...

//总的感觉是,我在在做阅读理解 
#include<bits/stdc++.h>
#define rs ((now<<1)|1)
#define INF 0x3f3f3f3f
#define ll long long
#define MAXN 500005
#define ls (now<<1)
using namespace std;
int n,m,a[MAXN];
struct node
{
       int val,cnt;
       //最小值,个数 
       ll sum;
       //区间和 
       int ask(int x) const
       {
               return (val==x)*cnt;
               //这一部分为了更新最小值个数 
       }
       void add(int x,int v)
       {
               if(v==val) val+=x,sum+=1ll*cnt*x;
               //这里是更新最小值,v是最小值 
               return ;
       }
       int operator < (const node &u) const
       {
               //更新一下最大值 
               return sum!=u.sum?sum<u.sum:cnt<u.cnt;
       }
       node operator + (const node &u) const
       {
            //更新区间最小值,区间加和
            //区间最小值个数 
               static node out;
               out.val=min(val,u.val);
               out.sum=sum+u.sum;
               out.cnt=ask(out.val)+u.ask(out.val);
               return out;
       }
}; 
struct Tree
{
       int Maxn,Mini,val,tag;
       //次小,最小,阈值,lz 
       node pre,nxt,sum,all;
       //前缀,后缀,全部,最大子段 
       void Init(int x)
       {
               Mini=x,Maxn=INF;
               sum=(node){x,1,(ll)x};
               //初始化
               if(x<=0)
            {
                  pre=nxt=all=(node){INF,0,0ll};
                  //我们递归,其实第一个值就是判断递归使用的
               //因为小于0,我们的值 
                  val=1;
                  //阈值 
               }
               else
               {
                  pre=nxt=all=(node){x,1,(ll)x};
                  val=INF;
               }
       }
       void add(int x)
       {
               //这里是更新最小值,下传tag的过程 
               pre.add(x,Mini);
               nxt.add(x,Mini);tag+=x; 
               sum.add(x,Mini);
               all.add(x,Mini);Mini+=x;
       }
}tr[MAXN<<2];
void cmin(int &x,int y)
{
     if(y-x>>31)x=y;
}
int clac(const node &x,const node &y,const node &z,int v,ll s=0,int d=0) 
{
    if((s=x.sum-y.sum-z.sum)>0&&(d=y.ask(v)+z.ask(v)-x.ask(v))) return min((ll)INF,s/d+1+v);
    return INF;
}
//Tree operator+(const Tree&x,const Tree&y){
//    static Tree out;
//    out.val=min(x.val,y.val),out.all=max(max(x.all,y.all),x.nxt+y.pre);
//    out.sum=x.sum+y.sum,out.pre=max(x.pre,x.sum+y.pre),out.nxt=max(x.nxt+y.sum,y.nxt);
//    if(x.Mini==y.Mini)out.Mini=x.Mini,out.Maxn=min(x.Maxn,y.Maxn);
//    else out.Mini=min(x.Mini,y.Mini),out.Maxn=min(min(x.Maxn,y.Maxn),max(x.Mini,y.Mini));
//    cmin(out.val,clac(x.pre,x.sum,y.pre,out.Mini));
//    cmin(out.val,clac(y.nxt,x.nxt,y.sum,out.Mini));
//    return out;
//}
Tree operator + (const Tree &x,const Tree &y)
{
    static Tree out;
//    out.val=min(x.val,y.val),out.all=max(max(x.all,y.all),x.nxt+y.pre);
//    out.sum=x.sum+y.sum,out.pre=max(x.pre,x.sum+y.pre),out.nxt=max(x.nxt+y.sum,y.nxt);
//    if(x.Mini==y.Mini)out.Mini=x.Mini,out.Maxn=min(x.Maxn,y.Maxn);
//    else out.Mini=min(x.Mini,y.Mini),out.Maxn=min(min(x.Maxn,y.Maxn),max(x.Mini,y.Mini));
//     Tree out;
     out.val=min(x.val,y.val);//更新阈值
     out.all=max(max(x.all,y.all),x.nxt+y.pre);
     out.sum=(x.sum+y.sum);
     out.pre=max(x.pre,x.sum+y.pre);
     out.nxt=max(x.nxt+y.sum,y.nxt);
     if(x.Mini==y.Mini)
     {
         out.Mini=x.Mini;
         out.Maxn=min(x.Maxn,y.Maxn);
     }
     else
     {
         out.Mini=min(x.Mini,y.Mini);
         out.Maxn=min(min(x.Maxn,y.Maxn),max(x.Mini,y.Mini));
     }
     cmin(out.val,clac(x.pre,x.sum,y.pre,out.Mini));
     cmin(out.val,clac(y.nxt,x.nxt,y.sum,out.Mini));
     return out;
}
void upd(int now)
{
     tr[now]=tr[ls]+tr[rs];
     //更新节点信息 
}

void pd(int now)
{
     if(tr[now].tag)
     {
         if(tr[ls].Mini==tr[now].Mini-tr[now].tag) tr[ls].add(tr[now].tag);
         if(tr[rs].Mini==tr[now].Mini-tr[now].tag) tr[rs].add(tr[now].tag);
         //线段树基操 
         tr[now].tag=0;
     }
}


void build(int now,int l,int r)
{
     if(l==r)
     {
         tr[now].Init(a[l]);
         //初始化 
         return ;
     }
     int mid=(l+r)>>1;
     build(ls,l,mid);
     build(rs,mid+1,r);
     upd(now);
}

void dfs(int now,int L,int R,int x)
{
     //这个是递归到阈值的过程  
//      cout<<L<<" "<<R<<" "<<x<<endl;
     if(x<=tr[now].Mini) return ;
     if(L==R) return tr[now].Init(x);
     if(x<tr[now].val&&x<tr[now].Maxn) 
     {
         return tr[now].add(x-tr[now].Mini);
         //如果阈值大于目前,就可以结束了
        //所以说,递归的条件是,大于阈值,所以阈值越小限制越多 
     }
     int mid=(L+R)>>1;
     pd(now);
     dfs(ls,L,mid,x),dfs(rs,mid+1,R,x);
     upd(now);
}

Tree query(int now,int L,int R,int l,int r)
{
//     cout<<tr[now].all.sum<<endl;
     if(L==l&&R==r)
     {
         return tr[now];
         //简单询问 
     }
     int mid=(L+R)>>1;
     pd(now);
     if(l>mid) return query(rs,mid+1,R,l,r);
     if(r<=mid) return query(ls,L,mid,l,r);
     return query(ls,L,mid,l,mid)+query(rs,mid+1,R,mid+1,r);
}

void change(int now,int L,int R,int l,int r,int x)
{
  
     if(l==L&&r==R)
     {
//         cout<<L<<" "<<R<<" "<<l<<" "<<r<<" "<<x<<endl;
         return dfs(now,l,r,x);
     } 
     int mid=(L+R)>>1;
     pd(now);
     if(l>mid) return change(rs,mid+1,R,l,r,x),upd(now);
     if(r<=mid) return change(ls,L,mid,l,r,x),upd(now);
     change(ls,L,mid,l,mid,x);
     change(rs,mid+1,R,mid+1,r,x);
     upd(now);
}
int opt,l,r,x;
int main()
{
//    freopen("c.in","r",stdin);
//    freopen("c.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    build(1,1,n);
    //建树过程
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&opt);
        if(opt==0)
        {
           scanf("%d%d%d",&l,&r,&x);
           change(1,1,n,l,r,x);
        }
        else
        {
            scanf("%d%d",&l,&r);
            cout<<query(1,1,n,l,r).all.sum<<endl;
        }
    }
}
//这代码确实很印度啊...

 

posted @ 2022-03-14 21:30  Authentic_k  阅读(59)  评论(1编辑  收藏  举报