省选摸你31

很没有自信,对待ZR的题,已经出现了阴影......

仍旧以为T1是结论题,边睡觉边等待灵光一现,结果两个小时过去了啥也没想出来,但是大题方向是对的,没找到排名和权值在线性基上的对应关系,额好像是知识漏洞

T2一开始看错题了,以为直接拿到了70分,然后知道了之后心态有点爆炸,一直认为如果得到了一个序列的\(\mathcal{O(n)}\)做法,那么这个题就可以切了,所以思路就一直在这上面,并且之前做过一道类似的是枚举中间的点的位置的,就一直没有转过弯来,后来考后经过zxb的指点,得到了预处理\(\mathcal{O(n^2)}\)的做法,启发,可以更换枚举方式,有奇效!!

T3是一点都不会,只有暴力分,剩下的甚至一点头绪都没有,树的做法是点分治,然而我并没有想到,以后注意树上的距离问题是可以很好的借助点分治来解决的,这里就利用了点分治来前缀优化建图,说实话之前用过一次了,我给忘记了...

T1 神必的集合

可以想得到是线性基,异或这东西,按位和线性基选一个就好了

考场上的思路不在找排名和权值的关系上,而在怎么统计方案数上,哎,以后做题不要着急的做题,而要先观察性质...

如果我们要求一个线性基上能得到的所有数中某一个数的权值,就把线性基有数的位置,注意是位置,对应在当前数的值提出来

组成一个二进制数,这个二进制数就是这个数的排名,证明的话,我们把线性基消成对角线型的,发现对于这个数来说要异或的就是对应的位置为1的位置上的数,额,不太好描述,算了算了自己baidu吧

而我们这个题要找的正是排名和权值的关系!!

我们现在有一堆排名和权值,我们将他们插入到线性基上,有一个结论,设\(rk_i\)表示排名为i的数,则有\(rk_i \text{^} rk_j=rk_{i \text{^}j}\)

这个根据排名得出的原因显然好吧...

所以我们在建立线性基的时候顺便把每一个位置的排名也搞出来,这样我们就得到了不超过n个排名和权值对应的关系

我们称线性基上有值的位为主元,此时我们的线性基上已经有几个位置是有值的了,我们称之为钦定的主元

那么我们当前的线性基一定是可行线性基的一个子线性基,这个定义挺模糊的,就是这个线性基再插入几个数可以得到可行线性基

而我们的钦定主元是由输入的数决定的,所以我们的钦定主元是不可变的,而非钦定主元后面的非主元是可以任意的,只要最后把其他主元消一下就好了,没有什么太大的关系

这样我们有了一个\(\mathcal{O(2^n*n)}\)的做法

直接枚举谁是主元,并且判断是否合法,必须含有钦定主元并且排名和权值能对上,那么这时的方案数就是2的所有非钦定主元的非主元个数次方,加起来就是答案

那么我们考虑这个过程,就是确定主元是谁的过程,如果是非钦定主元那就乘个系数

所以我们dp就行了,转移的时候看是不是非钦定主元,并且看排名和权值的这一位是否相同即可

AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=205;
const int mod=998244353;
int ksm(int x,int y){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
int n,mm,m,ans;
struct IFM{int x,y;}ifm[N];
int ji[N],cj;bool vis[N];
int mai[65],ba[65],rk[65];
int dp[65][65];
signed main(){
    freopen("set.in","r",stdin);
    freopen("set.out","w",stdout);
    n=read();mm=read();
    fo(i,1,mm){
        int x=read()-1,y=read();
        fu(j,n-1,0)if((y>>j)&1){
            if(ba[j])y^=ba[j],x^=rk[j];
            else {
                ifm[++m].x=rk[j]=x;
                ifm[m].y=ba[j]=y;
                break;
            }
        }
        if(y==0&&x!=0){printf("0");return 0;}
    }int mx=0;
    fo(i,1,m){
        fu(j,n-1,0)if((ifm[i].y>>j)&1){vis[j]=true;break;}
        int now=0;fu(j,n-1,0)if((ifm[i].x>>j)&1){now=j;break;}
        mx=max(mx,now+1);
    }
    bool fl=true;
    fo(k,1,m)if((ifm[k].x&1)!=(ifm[k].y&1))fl=false;
    if(fl){
        if(vis[0])dp[0][1]=1;
        else dp[0][1]=dp[0][0]=1;
    }
    else if(!vis[0])dp[0][0]=1;
    fo(i,0,n-2)fo(j,0,i+1){
        if(!dp[i][j])continue;
        fl=true;
        fo(k,1,m)if(((ifm[k].x>>j)&1)!=((ifm[k].y>>i+1)&1))fl=false;
        if(fl){
            if(vis[i+1])dp[i+1][j+1]=(dp[i+1][j+1]+dp[i][j])%mod;
            else {
                dp[i+1][j+1]=(dp[i+1][j+1]+dp[i][j]*ksm(2,i+1-j))%mod;
                dp[i+1][j]=(dp[i+1][j]+dp[i][j])%mod;
            }
        }
        else if(!vis[i+1])dp[i+1][j]=(dp[i+1][j]+dp[i][j])%mod;
    }
    fo(i,mx,n)ans=(ans+dp[n-1][i])%mod;
    printf("%lld",ans);
    return 0;
}
2^n
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=205;
const int mod=998244353;
int ksm(int x,int y){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
int n,mm,m,ans;
struct IFM{int x,y;}ifm[N];
bool com(IFM a,IFM b){return a.x<b.x;}
int ji[N],cj;bool vis[N];
int mai[65],ba[65],rk[65];
signed main(){
    freopen("set.in","r",stdin);
    freopen("set.out","w",stdout);
    n=read();mm=read();
    fo(i,1,mm){
        int x=read()-1,y=read();
        fu(i,n-1,0)if((y>>i)&1){
            if(ba[i])y^=ba[i],x^=rk[i];
            else {
                ifm[++m].y=ba[i]=y;
                ifm[m].x=rk[i]=x;
                break;
            }
        }
    }
    // cerr<<m<<endl;
    int u=(1<<n)-1,bas=0;
    // cerr<<u<<endl;
    fo(i,1,m){
        // cerr<<ifm[i].x<<" "<<ifm[i].y<<endl;
        fu(j,n-1,0)if((ifm[i].y>>j)&1){
            bas|=(1<<j);vis[j]=true;break;
        }
    }
    // cerr<<bas<<endl;
    for(int ss=u^bas,t=u^bas;ss>=0;ss--){
        if(ss<0)break;ss&=t;
        int s=u^ss;memset(mai,0,sizeof(mai));
        fu(i,n-1,0)if((s>>i)&1)mai[i]=1;//cerr<<i<<" ";cerr<<endl;
        bool fl=true;
        fo(i,1,m){
            int now=0;
            fu(j,n-1,0)if(mai[j])now=(now<<1)+((ifm[i].y>>j)&1);//cerr<<j<<" ";
            // cerr<<s<<" "<<ifm[i].y<<" "<<ifm[i].x<<" "<<now<<endl;
            if(now!=ifm[i].x){fl=false;break;}
        }
        if(!fl)continue;
        fo(i,1,n-1)mai[i]+=mai[i-1];//cerr<<mai[i]<<" ";cerr<<endl;
        // cerr<<s<<endl;
        int res=1;
        fo(i,1,n-1)if(mai[i]!=mai[i-1]&&!vis[i]){
            res=res*ksm(2,i-mai[i-1])%mod;
        }
        ans=(ans+res)%mod;
    }
    printf("%lld",ans);
    return 0;
}

T2 法阵

考场上一直想着枚举中间的数,用数据结构枚举两边的数

没想到正解是枚举前两个数,并且前两个数的选择种类是\(\mathcal{O(n)}\)的,因为前两个数中间不能有比这两个数大的数

如果有的话,那么好,这个数比你选的这两个优秀!!

于是我们用线段树把答案维护在最后一个数上,到时候直接查就行了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=5e5+5;
int n,a[N],T;
struct XDS{
    #define ls x<<1
    #define rs x<<1|1
    int mx[N*4],ms[N*4],tg[N*4];
    void pushup(int x){
        mx[x]=max(mx[ls],mx[rs]);
        ms[x]=max(ms[ls],ms[rs]);
        return ;
    }
    void pushdown(int x){
        ms[ls]=max(ms[ls],mx[ls]+tg[x]);
        tg[ls]=max(tg[ls],tg[x]);
        ms[rs]=max(ms[rs],mx[rs]+tg[x]);
        tg[rs]=max(tg[rs],tg[x]);
        tg[x]=0;return ;
    }
    void build(int x,int l,int r){
        if(l==r)return mx[x]=ms[x]=a[l],void();
        int mid=l+r>>1;
        build(ls,l,mid);build(rs,mid+1,r);
        pushup(x);return ;
    }
    void ins(int x,int l,int r,int ql,int qr,int v){
        if(ql>qr)return ;
        if(ql<=l&&r<=qr){
            ms[x]=max(ms[x],mx[x]+v);
            tg[x]=max(tg[x],v);return ;
        }
        int mid=l+r>>1;if(tg[x])pushdown(x);
        if(ql<=mid)ins(ls,l,mid,ql,qr,v);
        if(qr>mid)ins(rs,mid+1,r,ql,qr,v);
        pushup(x);return ;
    }
    int query(int x,int l,int r,int ql,int qr){
        if(ql>qr)return 0;
        if(ql<=l&&r<=qr)return ms[x];
        int mid=l+r>>1,ret=0;if(tg[x])pushdown(x);
        if(ql<=mid)ret=max(ret,query(ls,l,mid,ql,qr));
        if(qr>mid)ret=max(ret,query(rs,mid+1,r,ql,qr));
        pushup(x);return ret;
    }
    #undef ls
    #undef rs
}xds;
int sta[N],top,ans[N];
vector<int> vec[N];
struct Q{int x,y,id;}q[N];
bool com(Q a,Q b){return a.x<b.x;}
signed main(){
    freopen("fz.in","r",stdin);
    freopen("fz.out","w",stdout);
    n=read();
    fo(i,1,n){
        a[i]=read();
        while(top&&a[sta[top]]<=a[i]){vec[sta[top]].push_back(i);top--;}
        if(top)vec[sta[top]].push_back(i);sta[++top]=i;
    }
    xds.build(1,1,n);
    T=read();
    fo(i,1,T)q[i].x=read(),q[i].y=read(),q[i].id=i;
    sort(q+1,q+T+1,com);q[T+1].x=n+1;
    fu(i,T,1){
        if(q[i].x<q[i+1].x){
            fu(j,q[i+1].x-1,q[i].x){
                for(int k:vec[j]){
                    // cerr<<j<<" "<<k<<" "<<k-j+k<<endl;
                    xds.ins(1,1,n,k-j+k,n,a[j]+a[k]);
                }
            }
        }
        ans[q[i].id]=xds.query(1,1,n,q[i].x,q[i].y);
    }
    fo(i,1,T)printf("%lld\n",ans[i]);
}

T3 旅行

我们用点分治优化建图,树上距离可以用点分治

具体的操作办法就是,对于一个分治中心,建出深度个虚点,深度大的向深度小的连边,对应深度向对应点连边

然后点向对应的可以达到的深度的点连边,注意是跨过分治中心的深度

注意这样建图的条件是,自己和自己建立更长的边对答案没有影响

于是我们发现还有50条非树边,于是如果经过非树边那就一定经过这个非树边的某一个节点,我们把这样的50个节点看成点分治的分治中心做就好了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=2e5+55;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,m,d[N],c[N];
struct E{int to,nxt;}e[N*2];
int head[N],rp=1,vie[N*2];
void add_edg(int x,int y){e[++rp].to=y;e[rp].nxt=head[x];head[x]=rp;}
struct DIJ{
    struct E{int to,nxt,val;}e[N*200];
    int head[N*200],rp,seg;
    void add_edg(int x,int y,ll z){
        e[++rp].to=y;e[rp].nxt=head[x];
        e[rp].val=z;head[x]=rp;
    }
    struct node{
        int x;ll ds;node(){}node(int a,ll b){x=a;ds=b;}
        bool operator < (node a)const{return ds>a.ds;}
    };
    priority_queue<node> q;
    ll dis[N*200];
    bool vis[N*200];
    void dij(){
        memset(dis,0x3f,sizeof(dis));
        q.push(node(1,0));dis[1]=0;
        while(!q.empty()){
            int x=q.top().x;q.pop();
            if(vis[x])continue;vis[x]=true;
            for(int i=head[x];i;i=e[i].nxt){
                int y=e[i].to;
                if(dis[y]<=dis[x]+e[i].val)continue;
                dis[y]=dis[x]+e[i].val;
                q.push(node(y,dis[y]));
            }
        }
        fo(i,2,n)printf("%lld\n",dis[i]);
    }
}dij;
int rt,mx,ms[N],siz[N],bia[N];
bool vis[N];
void findrt(int x,int f,int sz){
    siz[x]=1;ms[x]=0;
    for(int i=head[x];i;i=e[i].nxt){
        if(vie[i])continue;
        int y=e[i].to;
        if(y==f||vis[y])continue;
        findrt(y,x,sz);
        siz[x]+=siz[y];
        ms[x]=max(ms[x],siz[y]);
    }
    ms[x]=max(ms[x],sz-siz[x]);
    if(ms[x]<mx)rt=x,mx=ms[x];
}
int dep[N],mxd,ji[N],cj,vib[N];
void bfs(int x,int tp){
    cj=mxd=0;
    queue<int> q;while(!q.empty())q.pop();
    q.push(x);dep[x]=0;vib[x]=true;
    while(!q.empty()){
        int x=q.front();q.pop();
        ji[++cj]=x;mxd=dep[x];
        for(int i=head[x];i;i=e[i].nxt){
            if(vie[i]&&tp)continue;
            int y=e[i].to;
            if(vis[y]||vib[y])continue;
            dep[y]=dep[x]+1;
            q.push(y);vib[y]=true;
        }
    }
    fo(i,1,cj)vib[ji[i]]=false;
}
void sol(int x,int tp=1){
    bfs(x,tp);bia[0]=x;
    fo(i,1,mxd){
        bia[i]=++dij.seg;
        dij.add_edg(bia[i],bia[i-1],0);
    }
    fo(i,1,cj){
        dij.add_edg(bia[dep[ji[i]]],ji[i],0);
        if(d[ji[i]]>=dep[ji[i]])dij.add_edg(ji[i],bia[min(mxd,d[ji[i]]-dep[ji[i]])],c[ji[i]]);
    }
}
void dive(int x,int sz){
    sol(x);vis[x]=true;
    for(int i=head[x];i;i=e[i].nxt){
        if(vie[i])continue;
        int y=e[i].to;
        if(vis[y])continue;mx=inf;
        if(siz[y]>siz[x])findrt(y,x,sz-siz[x]),dive(rt,sz-siz[x]);
        else findrt(y,x,siz[y]),dive(rt,siz[y]);
    }
}
int xh[N];
void dfs(int x,int f){
    xh[x]=xh[f]+1;
    for(int i=head[x];i;i=e[i].nxt){
        if(vie[i])continue;
        int y=e[i].to;
        if(y==f)continue;
        if(xh[y]&&xh[y]<xh[x]){vie[i]=vie[i^1]=true;continue;}
        dfs(y,x);
    }
}
signed main(){
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    n=dij.seg=read();m=read();
    fo(i,1,m){
        int x=read(),y=read();
        add_edg(x,y);add_edg(y,x);
    }
    dfs(1,0);
    fo(i,1,n)d[i]=read(),c[i]=read();
    mx=inf;findrt(1,0,n);dive(rt,n);
    cerr<<dij.seg<<" "<<dij.rp<<endl;
    memset(vis,false,sizeof(vis));
    cerr<<1.0*clock()/CLOCKS_PER_SEC<<endl;
    fo(i,1,rp)if((!(i&1))&&vie[i])sol(e[i].to,0);
    cerr<<1.0*clock()/CLOCKS_PER_SEC<<endl;
    dij.dij();cerr<<dij.seg<<" "<<dij.rp<<endl;
}
posted @ 2022-03-14 21:07  fengwu2005  阅读(31)  评论(0编辑  收藏  举报