ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

3.21省选模拟

2022-03-21 18:35:48  阅读:146  来源: 互联网

标签:std return 省选 top reach 3.21 int now 模拟


Just hide this love in my heart,forever

一言难尽,$T1$细节巨多,想出来就没想写的欲望,$T2$想写个$tarjan$每次暴力缩写挂了,$T3$人类智慧题,成功垫底

T1

//首先想一下怎么找到当前串在原串的所有位置
//考场上一个没做出来很大的原因就是并不会找到这个位置,其实考场上分讨已经解决了 
//我们需要得到当前串所有出现位置的right集合
//一个串的right集合就是这个串对应节点的right集合
//就是endpos集合了,前几天刚写了,建出来后缀树之后
//所有的子树的位置,对于fa建反向边,然后子树内部都是了
//考虑完出现位置,就是exr的分讨了
//考试时候我多想了一种情况 
//其实只需要讨论最左边和最右边有没有相交就好了
//因为不合法的方案的计算方式是一样的
//首先有三块的话必然0了 
//Sit1:相交
//其实考虑i往右移动的贡献计算方式会好算点(其实在考场上这道题也是很可做的) 
//首先[1,l[1]),其余的只能放在中间
//[l[i],l[i+1]),[l[i+1],r[i+1])任取就好了
//[l[m],r[1]),这部分取右边所有就好了 
//最后的答案就是合并一下
//Sit2:不相交
//还是考虑往右扫的过程
//如果还是在1的长度范围内
//按照1的式子继续推就好了
//否则找出前驱后继更新就好了
//线段树维护最大值,最小值,和 
//其实线段树下标是一个当前结尾串的的结束位置
//然后根据SAM的关系,我们找到所有有值的位置就好了 
#include<bits/stdc++.h>
#define INF 2147483647
#define int long long
#define MAXN 200005
using namespace std;
struct Node
{
       int Min,Max;
       int Sum1,Sum2;
}tr[MAXN<<5];
struct Tree
{
       int ch[MAXN<<5][2],tot;
       Node Merge(const Node &a,const Node &b)
       {
               Node c;
               c.Min=min(a.Min,b.Min);
               c.Max=max(a.Max,b.Max);
               c.Sum1=a.Sum1+b.Sum1+b.Min*(b.Min-a.Max);
               c.Sum2=a.Sum2+b.Sum2+b.Min-a.Max;
               return c;
       }
       int Find_Max(int now,int l,int r,int L,int R)
       {
              if(!now) return 0;
              if(L<=l&&r<=R) return tr[now].Max;
              int mid=(l+r)>>1,res=0;
              if(L<=mid) res=Find_Max(ch[now][0],l,mid,L,R);
              if(R>mid)  res=max(res,Find_Max(ch[now][1],mid+1,r,L,R));
              return res;
       }
       int Find_Min(int now,int l,int r,int L,int R)
       {
              if(!now) return INF;
              if(L<=l&&r<=R) return tr[now].Min;
              int mid=(l+r)>>1,res=INF;
              if(L<=mid) res=Find_Min(ch[now][0],l,mid,L,R);
              if(R>mid)  res=min(res,Find_Min(ch[now][1],mid+1,r,L,R));
              return res;
       }
       Node tmp;
       void query(int now,int l,int r,int L,int R)
       {
               if(!now) return ;
               if(L<=l&&r<=R)
               {
                  if(tmp.Min==0) tmp=tr[now];
                  else tmp=Merge(tmp,tr[now]);
                  return ;
               }
               int mid=(l+r)>>1;
               if(L<=mid) query(ch[now][0],l,mid,L,R);
               if(R>mid)  query(ch[now][1],mid+1,r,L,R);
       }
       void change(int &now,int l,int r,int p)
       {
               if(!now) now=++tot;
               if(l==r)
               {
                  tr[now].Min=tr[now].Max=p;
                  tr[now].Sum1=tr[now].Sum2=0;
                  return ;
               }
               int mid=(l+r)>>1;
               if(p<=mid) change(ch[now][0],l,mid,p);
               else change(ch[now][1],mid+1,r,p);
               if(ch[now][0]&&ch[now][1]) tr[now]=Merge(tr[ch[now][0]],tr[ch[now][1]]);
               else if(ch[now][0]) tr[now]=tr[ch[now][0]];
               else tr[now]=tr[ch[now][1]];
       }
       int Merge_num(int x,int y)
       {
              if(!x||!y) return x+y;
              int now=++tot;
              ch[now][0]=Merge_num(ch[x][0],ch[y][0]);
              ch[now][1]=Merge_num(ch[x][1],ch[y][1]);
              if(ch[now][0]&&ch[now][1]) tr[now]=Merge(tr[ch[now][0]],tr[ch[now][1]]);
              else if(ch[now][0]) tr[now]=tr[ch[now][0]];
              else tr[now]=tr[ch[now][1]];
              return now;
       }
}T;
int ls=1,cnt=1,zx[MAXN][20],fa[MAXN],len[MAXN],pos[MAXN],trs[MAXN][26];
void Insert(int c,int id)
{
     int p=ls;
     ls=++cnt;
     int now=cnt;
     pos[id]=cnt;
     len[now]=len[p]+1;
     for(;p&&!trs[p][c];p=fa[p]) trs[p][c]=now;
     if(!p) fa[now]=1;
     else
     {
         int q=trs[p][c];
         if(len[p]+1==len[q]) fa[now]=q;
         else
         {
               int spilt=++cnt;
              for(int i=0;i<=25;i++)
               {
                     trs[spilt][i]=trs[q][i];
               }
               fa[spilt]=fa[q];
               len[spilt]=len[p]+1;
               fa[now]=fa[q]=spilt;
               //now长,分裂出来了spilt 
               for(;p&&trs[p][c]==q;p=fa[p]) trs[p][c]=spilt;
         }
     }
}
int head[MAXN],nxt[MAXN],to[MAXN],rt[MAXN],tot1,n;
void add(int u,int v)
{
     tot1++;
     to[tot1]=v;
     nxt[tot1]=head[u];
     head[u]=tot1;
}
void dfs(int now)
{  
//     cout<<"now: "<<now<<endl;
//     system("pause");
     zx[now][0]=fa[now];
     for(int i=1;i<=18;i++) zx[now][i]=zx[zx[now][i-1]][i-1];
     for(int i=head[now];i!=-1;i=nxt[i])
     {
          int y=to[i];
//          dep[y]=dep[now]+1;
          dfs(y);
          rt[now]=T.Merge_num(rt[now],rt[y]);
     }
}
void build()
{
     memset(head,-1,sizeof(head));
     for(int i=1;i<=cnt;i++)
     {
          if(fa[i]) add(fa[i],i);//fa不是endpos大的节点吗,越来越懵了.
     }
     for(int i=1;i<=n;i++) T.change(rt[pos[i]],1,n,i);
     //pos是第i个后缀在SAM的节点编号 
     dfs(1);
     //子树的集合和就是我们当前节点的终止位置集合 
}
int C(int x)
{
    if(x<2) return 0;
    return x*(x-1)/2;
} 
int query(int l,int r)
{
    int Len=r-l+1,u=pos[r];
    for(int i=18;i>=0;i--)if(len[zx[u][i]]>=Len)u=zx[u][i];
  
    //这个是找串的位置 
    int L=tr[rt[u]].Min,R=tr[rt[u]].Max;//找到当前串的最大最小值  
    if(L<R-Len*2+1&&T.Find_Max(rt[u],1,n,L,R-Len)-Len+1>L)return C(n-1);
    if(R-Len+1<=L)
    {
        Node now=tr[rt[u]];
        int lm=R-Len+1;
        long long ans=now.Sum1-now.Sum2*lm+C(L-lm)+(L-lm)*1LL*(n-Len);
        return C(n-1)-ans;
    }
    else 
    {
        T.tmp=(Node){0,0,0,0};
        int lm=R-Len+1,poslm=T.Find_Max(rt[u],1,n,1,lm);
        T.query(rt[u],1,n,poslm,L+Len-1);
        Node now=T.tmp;
        int p1=T.Find_Max(rt[u],1,n,1,L+Len-1);
        int p2=T.Find_Min(rt[u],1,n,L+Len,n);
        long long ans=now.Sum1-now.Sum2*lm+(p2>lm?(L-(p1-Len+1))*1LL*(p2-lm):0);
        return C(n-1)-ans;
    }
}
int q,l,r;
char s[MAXN];
signed main()
{
    scanf("%lld%lld",&n,&q);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
    {
        Insert(s[i]-'0',i);
    }
    build();
    for(int i=1;i<=q;i++)
    {
        cin>>l>>r;
        cout<<query(l,r)<<"\n";
    }
} 

T2

//考场上写的暴力缩点然后看联通块最小值,期望能拿60,没调出来就寄了
//那么正解是什么,我们考虑.如果存在指向关系,如果不能回来的话,就指向别人的点必然不优
//我们考虑维护一堆强连通分量,其实最后有用的只有没有出度的一个强连通分量
//我们可以用栈维护强连通分量,每次加入新点,如果被删过了,全删掉 
//如果被来过,并入联通块,否则判断能否回来
#include <vector>
#include <set>
#include "keys.h"
using namespace std;
const int N=1000005;
set<pair<int,int> > g[N];
set<int> have[N];
vector<int> reach[N];
int n,m,fa[N],sz[N],in[N],tag[N],sta[N],top;
void ckMin(int &p,int q){p=(p<q?p:q);}
int fd(int x){return fa[x]^x?fa[x]=fd(fa[x]):x;}
void mer(int u,int v){
    u=fd(u),v=fd(v);
    //合并俩联通块 
    if(g[u].size()+have[u].size()+reach[u].size()>g[v].size()+have[v].size()+reach[v].size()) std::swap(u,v);
    fa[u]=v,sz[v]+=sz[u];
    //把u连到v上
    for(const auto&p : reach[u]) reach[v].push_back(p);
    reach[u].clear();
    for(const auto&p : have[u]){
        auto pos=g[v].lower_bound(make_pair(p,0));
        while(pos!=g[v].end()&&pos->first==p) reach[v].push_back(pos->second),pos=g[v].erase(pos);
        //删边操作,,把联通块内的边删掉 
        have[v].insert(p);
    }
    have[u].clear();
    for(const auto&p : g[u])
        if(have[v].count(p.first)) reach[v].push_back(p.second);
        else g[v].insert(p);
    g[u].clear();
    //向外连边 
}
void run(int x){
    //这应该就是缩点的过程 
    //每次都能处理完一个联通块 
    sta[++top]=x,tag[x]=1;
    while(top){
        if(reach[x=sta[top]].empty()){tag[x]=2,--top;continue;}
        //如果这个时候没有出边了,证明这个点可能成为答案
        //所有连向他的点不会更优,要么这个点存在联通块内
        //这个点必须是要有向外的边的,否则必然不可能出去了 
        int u=fd(reach[x].back());reach[x].pop_back();
        if(!tag[u]){sta[++top]=u,tag[u]=1;continue;}
        //如果没有加入的话,就标记上,最后再看能不能进去 
        if(tag[u]==2) continue;
        while(sta[top]!=u) mer(x,sta[--top]);
        //这个是能回来的情况,就把所有的一个块合并了 
        sta[top]=fd(x);
    }
}
void ck(int u,int v,int col){
    u=fd(u),v=fd(v);
    if(u==v) return;
    in[u]+=(have[u].count(col)?1:0);
    in[v]+=(have[v].count(col)?1:0);
}
std::vector<int> find_reachable(std::vector<int> r, std::vector<int> u, std::vector<int> v, std::vector<int> c) {
    std::vector<int> ans(r.size(), 0);
    n=r.size(),m=u.size();int i,mn=1e9;
    //r每个房间钥匙
    //m边数,u,v,c点和边权 
    for(i=0;i<m;++i){
        //第一步建边
        //初始化能到的点
        //显然的,只需要先把能用的边建出来 
        if(c[i]==r[u[i]]) reach[u[i]].push_back(v[i]);
        else g[u[i]].insert(make_pair(c[i],v[i]));
        if(c[i]==r[v[i]]) reach[v[i]].push_back(u[i]);
        else g[v[i]].insert(make_pair(c[i],u[i]));
    }
    for(i=0;i<n;++i) fa[i]=i,sz[i]=1,have[i].insert(r[i]);
    //初始化强连通分量大小 
    for(i=0;i<n;++i) if(!tag[i]) run(i);//处理若干个联通块 
    //开始计算 
    for(i=0;i<m;++i) ck(u[i],v[i],c[i]);
    for(i=0;i<n;++i) if(!in[fd(i)]) ckMin(mn,sz[fd(i)]);
    for(i=0;i<n;++i) if(!in[fd(i)]) ans[i]=(sz[fd(i)]==mn);
    return ans;
}

 

T3

#include<bits/stdc++.h>
#include"dna.h"
#define MAXN 1000005
using namespace std; 
int n,rev[100],cn[MAXN][3][3],c[3][3];
void init(string a,string b) 
{
    n=a.size();
    rev['A']=0;
    rev['C']=1;
    rev['T']=2;
    for(int i=1;i<=n;++i)
    {
        for(int j=0;j<3;++j)
        {    
            for(int k=0;k<3;++k)
            {
                cn[i][j][k]=cn[i-1][j][k];
            }
        }
        ++cn[i][rev[a[i-1]]][rev[b[i-1]]];
        //无非是记录一下前缀和罢了 
    }
}
int get_distance(int x, int y) 
{
    ++x,++y;
    //向后错一位 
    for(int i=0;i<3;++i)
    {
        for(int j=0;j<3;++j)
        {
            c[i][j]=cn[y][i][j]-cn[x-1][i][j];//记录对数
        }
    }
    //c[i][j]表示前面为i,后面为j的对数有多少
    //c[0][1]+c[2][1]表示后面的1的个数
    //c[1][0]+c[1][2]表示前面的1的个数
    //c[0][1]+c[0][2]表示前面的0的个数
    //c[1][0]+c[2][0]表示后面的0的个数
    //既然0,1都一样了,2必然一样了    
    if(c[0][1]+c[2][1]!=c[1][0]+c[1][2] || c[0][1]+c[0][2]!=c[1][0]+c[2][0]) return -1;
    //这时候找最少的距离
    //显然这三种情况分别是各自换到各自位置
    //首先不同的有多少个就换多少个,发现会有重复替换的,恰好是相同两种的差的绝对值,就是说你交换完一种情况
    //如果较优的话会帮忙交换另外的几种,剩下的两种就各自分各自的了 
    return max(c[0][1],c[1][0])+max(c[0][2],c[2][0])+max(c[1][2],c[2][1])-abs(c[0][1]-c[1][0]);
}

 

标签:std,return,省选,top,reach,3.21,int,now,模拟
来源: https://www.cnblogs.com/Eternal-Battle/p/16035898.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有