「GXOI / GZOI2019」简要题解

「GXOI / GZOI2019」简要题解

LOJ#3083. 「GXOI / GZOI2019」与或和

https://loj.ac/problem/3083

题意:求一个矩阵的所有子矩阵的与和 和 或和。

分析:

  • 或和与是一个东西,只要把所有数都异或上\((1<<31)-1\)然后再从总答案中减掉就能互相转化,考虑求与。
  • 枚举每一位,转化成算有多少个全\(1\)子矩形,单调栈经典问题。总时间复杂度\(\mathrm{O}(n^2\log n)\)

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
typedef long long ll;
#define N 1050
#define mod 1000000007
int mask=(1ll<<31)-1,n,a[N][N];
int b[N][N],up[N],S[N],tp;
ll work() {
    int i,j,k;
    ll re=0;
    for(k=0;k<=30;k++) {
        for(i=1;i<=n;i++) up[i]=0;
        for(i=1;i<=n;i++) {
            ll now=0;
            tp=0;
            S[0]=0;
            for(j=1;j<=n;j++) {
                b[i][j]=a[i][j]&(1<<k);
                if(!b[i][j]) up[j]=0;
                else up[j]++;
                if(!b[i][j]) tp=0,S[0]=j,now=0;
                else {
                    while(tp&&up[S[tp]]>=up[j]) now-=ll(up[S[tp]])*(S[tp]-S[tp-1]),tp--;
                    S[++tp]=j; now+=ll(up[j])*(S[tp]-S[tp-1]);
                    now%=mod; re=(re+now*(1<<k))%mod;
                }
            }
        }
    }
    return re%mod;
}
int main() {
    scanf("%d",&n);
    ll tot=0;
    int i,j;
    for(i=1;i<=n;i++) for(j=1;j<=n;j++) {
        scanf("%d",&a[i][j]);
        tot=(tot+i*j);
    }
    tot%=mod;
    ll ans1=work();
    for(i=1;i<=n;i++)for(j=1;j<=n;j++)a[i][j]^=mask;
    ll ans2=work();
    ans2=(tot*mask%mod-ans2)%mod;
    printf("%lld %lld\n",(ans1+mod)%mod,(ans2+mod)%mod);
}

LOJ#3084. 「GXOI / GZOI2019」宝牌一大堆

https://loj.ac/problem/3084

分析:

  • 对于七对子和国土无双,可以拿出来\(\mathrm{O}(13^2+35\log 35)\) 处理。
  • 剩下的情况,可以看成是\(1\)组雀头和\(4\)组杠子或面子。
  • 直接设\(f[i][j][k][l][x][y]\)表示前\(i\)张牌,\(i-2,i-1,i\)分别作为顺子的最后一张拿走了\(j,k,l\)张牌,总共的杠子+面子数为\(x\),雀头数为\(y\)
  • 然后\(l\)这维不用存并且\(i\)可以滚动掉,同时可以发现这个是跑不满的。
  • 注意转移的时候不要超过\(1\)\(4\)的限制。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
#define N 55
ll mi[N],C[N][N];
int a[N],b[N];
inline void upd(ll &x,ll y) {x=x>y?x:y;}
int trans(char *w) {
    if(strlen(w+1)==2) {
        int i;
        if(w[2]=='m') i=1;
        else if(w[2]=='p') i=2;
        else i=3;
        return (i-1)*9+w[1]-'0';
    }else {
        if(w[1]=='E') return 28;
        if(w[1]=='S') return 29;
        if(w[1]=='W') return 30;
        if(w[1]=='N') return 31;
        if(w[1]=='Z') return 32;
        if(w[1]=='B') return 33;
        if(w[1]=='F') return 34;
    }
    return -1;
}
ll ch(int x,int v) {
    return C[a[x]][v]*mi[b[x]*v];
}
ll f[35][3][3][5][2];
ll wk1() {
    memset(f,0,sizeof(f));
    f[0][0][0][0][0]=1;
    int i,j,k,x,y,z,w;
    for(i=0;i<35;i++) {
        for(j=0;j<3;j++) if(!j||(i<=27&&i%9!=0&&i%9!=1)) {
            for(k=0;k<3;k++) if(!k||(i<=27&&i%9!=8&&i%9!=0)) if(a[i+1]>=j+k) {
                for(x=j+k;x<=4;x++) {
                    for(y=0;y<2;y++) if(f[i][j][k][x][y]) {
                        for(z=0;z<=2&&j+k+z<=a[i+1]&&x+z<=4;z++) {
                            for(w=0;j+k+z+w*3<=a[i+1]&&x+z+w<=4;w++) {
                                int t=j+k+z+w*3;
                                upd(f[i+1][k][z][x+z+w][y],f[i][j][k][x][y]*ch(i+1,t));
                                if(!y&&t+2<=a[i+1]) upd(f[i+1][k][z][x+z+w][1],f[i][j][k][x][y]*ch(i+1,t+2));
                            }
                        }
                        if(a[i+1]-j-k==4&&x<4) upd(f[i+1][k][0][x+1][y],f[i][j][k][x][y]*ch(i+1,4));
                    }
                }
            }
        }
    }return f[34][0][0][4][1];
}
ll c[N];
ll wk2() {
    int i;
    for(i=1;i<=34;i++)c[i]=ch(i,2);
    sort(c+1,c+35);ll re=1;
    for(i=28;i<=34;i++)re*=c[i];return re*7;
}
int d[20]={0,1,9,10,18,19,27,28,29,30,31,32,33,34};
ll wk3() {
    ll re=0;int i,j;
    for(i=1;i<=13;i++) {
        ll tmp=ch(d[i],2);for(j=1;j<=13;j++)if(i!=j)tmp*=ch(d[j],1);re=max(re,tmp);
    }return re*13;
}
void solve() {
    int i,j;
    for(i=1;i<=34;i++) a[i]=4,b[i]=0;
    for(mi[0]=i=1;i<=18;i++) mi[i]=mi[i-1]<<1;;
    for(i=0;i<=4;i++)for(C[i][0]=j=1;j<=i;j++)C[i][j]=C[i-1][j]+C[i-1][j-1];
    while(1) {
        char w[10];
        scanf("%s",w+1);
        if(w[1]=='0') break;
        a[trans(w)]--;
    }
    while(1) {
        char w[10];
        scanf("%s",w+1);
        if(w[1]=='0') break;
        b[trans(w)]=1;
    }
    printf("%lld\n",max(wk1(),max(wk2(),wk3())));
}
int main() {
    int T;
    scanf("%d",&T);
    while(T--) solve();
}

LOJ#3085. 「GXOI / GZOI2019」特技飞行

https://loj.ac/problem/3085

分析:

  • 注意到\(c\)\(a,b\)无关,直接数个点就行了。
  • 然后我们感性理解一下如果所有特技都「对向交换」,那么一定是合法的。
  • 于是就有了一个极值,另一个极值是要最小化「对向交换」的数量。
  • 考虑排序后的数组的每个置换,可以发现最少交换次数为置换大小-1,且不同置换之间不影响,那么这部分的数量就是n-置换数。
  • (bit还得拆成四个精度搞来搞去太麻烦,直接上kdt跑得还挺快)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <cmath>
using namespace std;
typedef long long ll;
typedef double f2;
#define N 500050
const f2 eps = 1e-10;
int n,Y0[N],Y1[N],st,ed,is[N],tot,id[N],to[N],K,vis[N];
ll A,B,C;
char buf[100000],*p1,*p2;
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd() {
    int x=0; char s=nc();
    while(s<'0') s=nc();
    while(s>='0') x=(((x<<2)+x)<<1)+s-'0',s=nc();
    return x;
}
set<pair<int,int> >S;
set<pair<int,int> >::iterator it;
f2 mn[N][2],mx[N][2];
int flg[N],wh,ch[N][2],ok[N];
struct Point {
    f2 p[2];
    Point() {}
    Point(f2 x_,f2 y_) {p[0]=x_,p[1]=y_;}
    bool operator < (const Point &u) const {
        return p[wh]==u.p[wh]?p[!wh]<u.p[!wh]:p[wh]<u.p[wh];
    }
}a[N];
bool cmp(int x,int y) {
    return Y1[x]<Y1[y];
}
f2 Abs(f2 x) {return x>0?x:-x;}
void pushup(int p,int x) {
    mn[p][0]=min(mn[p][0],mn[x][0]);
    mn[p][1]=min(mn[p][1],mn[x][1]);
    mx[p][0]=max(mx[p][0],mx[x][0]);
    mx[p][1]=max(mx[p][1],mx[x][1]);
}

int build(int l,int r,int f,int k) {
    if(l>r) return 0;
    int mid=(l+r)>>1;
    wh=k;
    nth_element(a+l,a+mid,a+r+1);
    int p=mid;
    mn[p][0]=mx[p][0]=a[p].p[0];
    mn[p][1]=mx[p][1]=a[p].p[1];
    if(l<mid) ch[p][0]=build(l,mid-1,p,!k),pushup(p,ch[p][0]);
    if(r>mid) ch[p][1]=build(mid+1,r,p,!k),pushup(p,ch[p][1]);
    return p;
}
void update(int x,int y,int z,int p) {
    if(!p||flg[p]||x+z<mn[p][0]||x-z>mx[p][0]||y+z<mn[p][1]||y-z>mx[p][1]) return ;
    //if(!p) return ;
    if(max(max(max(abs(x-mn[p][0]),abs(x-mx[p][0])),abs(y-mn[p][1])),abs(y-mx[p][1]))-eps<z) {
        flg[p]=1; return ;
    }
    if(!ok[p]&&max(abs(a[p].p[0]-x),abs(a[p].p[1]-y))-eps<z) {
        ok[p]=1;
    }
    update(x,y,z,ch[p][0]); update(x,y,z,ch[p][1]);
}
int dfs(int p) {
    if(flg[p]) {
        ok[p]=1; 
        ok[ch[p][0]]=ok[ch[p][1]]=flg[ch[p][0]]=flg[ch[p][1]]=1;
    }
    int re=ok[p];
    if(ch[p][0]) re+=dfs(ch[p][0]);
    if(ch[p][1]) re+=dfs(ch[p][1]);
    return re;
}
int main() {
    scanf("%d%lld%lld%lld%d%d",&n,&A,&B,&C,&st,&ed);
    int i,j;
    for(i=1;i<=n;i++) Y0[i]=rd();
    for(i=1;i<=n;i++) Y1[i]=rd();
    for(i=n;i;i--) {
        for(it=S.begin();it!=S.end()&&(it->first < Y1[i]);it++) {
            j=it->second;
            f2 t=f2(Y0[j]-Y0[i])/(Y1[i]-Y1[j]);
            f2 x=(t*ed+st)/(t+1);
            f2 y=(t*Y1[j]+Y0[j])/(t+1);
            f2 tx=x,ty=y;
            x=tx+ty;
            y=tx-ty;
            a[++tot]=Point(x,y);
        }
        S.insert(make_pair(Y1[i],i));
    }
    int rt=build(1,tot,0,0);
    K=rd();
    for(i=1;i<=K;i++) {
        int x,y,z;
        x=rd(),y=rd(),z=rd();
        int tx=x,ty=y;
        x=tx+ty;
        y=tx-ty;
        update(x,y,z,rt);
    }
    ll ans=dfs(rt)*C;
    ll ans1=ans+tot*A,ans2=ans+tot*A;
    for(i=1;i<=n;i++) id[i]=i;
    sort(id+1,id+n+1,cmp);
    ll u=n;
    for(i=1;i<=n;i++) if(!vis[i]) {
        u--;
        for(j=i;!vis[j];j=id[j]) vis[j]=1;
    }
    ans2+=(tot-u)*(B-A);
    if(ans1>ans2) swap(ans1,ans2);
    printf("%lld %lld\n",ans1,ans2);
}

LOJ#3086. 「GXOI / GZOI2019」逼死强迫症

https://loj.ac/problem/3086

分析:

  • \(f_{i,j}\)表示有\(i\)列,放了\(j\)\(1\times 1\)的格子的方案数,特别的我们令\(j=1\)的情况下表示\(i-1\)列多出来一个格子。
  • 转移推推就可以了
  • \(f_{i,0}=f_{i-1,0}+f_{i-2,0}\)
  • \(f_{i,1}=f_{i-1,0}+f_{i-2,0}+f_{i-2,1}\)
  • \(f_{i,2}=f_{i-1,2}+f_{i-2,2}+2f_{i-2,1}\)
  • 然后搞一个\(6\times 6\)的矩阵矩乘一下就行了。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <queue>
using namespace std;
#define N 1000050
#define mod 1000000007
typedef long long ll;
struct Mat {
    ll a[6][6];
    Mat() {memset(a,0,sizeof(a));}
    Mat operator * (const Mat &u) const {
        Mat re; int i,j,k;
        for(i=0;i<6;i++)for(k=0;k<6;k++)for(j=0;j<6;j++) {
            re.a[i][j]+=a[i][k]*u.a[k][j];
        }
        for(i=0;i<6;i++)for(j=0;j<6;j++)re.a[i][j]%=mod;
        return re;
    }
}G[33];
Mat qp(Mat x,int y) {
    Mat I;
    int i;
    for(i=0;i<6;i++) I.a[i][i]=1;
    for(i=0;i<=30;i++) if(y&(1<<i)) I=I*G[i];
    return I;
}
int main() {
    int i;
    Mat x;
    int t;
    x.a[0][0]=x.a[0][1]=x.a[1][0]=x.a[2][0]=x.a[2][1]=x.a[2][3]=1;
    x.a[3][2]=x.a[4][4]=x.a[4][5]=x.a[5][4]=1;
    x.a[4][3]=2;
    G[0]=x;
    for(i=1;i<=30;i++) G[i]=G[i-1]*G[i-1];
    scanf("%d",&t);
    while(t--) {
        int y;scanf("%d",&y);
        if(y<=2) {puts("0"); continue;}
        Mat t=qp(x,y-1);
        ll ans=(t.a[4][0]+t.a[4][1]+t.a[4][2]);
        printf("%lld\n",ans%mod);
    }
}

LOJ #3087. 「GXOI / GZOI2019」旅行者

https://loj.ac/problem/3087

题意:有向图,给K个点,求两两之间最短的最短路。

分析:

  • 二进制分组,然后跑最短路即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <queue>
using namespace std;
#define N 100050
#define M 600050
char buf[100000],*p1,*p2;
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd() {
    int x=0; char s=nc();
    while(s<'0') s=nc();
    while(s>='0') x=(((x<<2)+x)<<1)+s-'0',s=nc();
    return x;
}
typedef long long ll;
int head[N],to[M],nxt[M],val[M],vis[N],n,cnt,S,m,K,a[N];
ll dis[N];
priority_queue<pair<ll,int> >q;
inline void add(int u,int v,int w) {
    to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; val[cnt]=w;
}
void dij() {
    int i;
    memset(dis+1,0x3f,S<<3);
    memset(vis+1,0,S<<2);
    dis[S]=0;
    q.push(make_pair(0,S));
    while(!q.empty()) {
        int x=q.top().second; q.pop(); 
        if(vis[x]) continue;
        vis[x]=1;
        for(i=head[x];i;i=nxt[i]) if(dis[to[i]]>dis[x]+val[i]) {
            dis[to[i]]=dis[x]+val[i];
            q.push(make_pair(-dis[to[i]],to[i]));
        }
    }
}
void solve() {
    n=rd(),m=rd(),K=rd();
    int i,x,y,z,j;
    S=n+1;
    memset(head+1,0,S<<2);
    cnt=0;
    for(i=1;i<=m;i++) {
        x=rd(),y=rd(),z=rd();
        add(x,y,z);
    }
    for(i=1;i<=K;i++) a[i]=rd();
    random_shuffle(a+1,a+K+1);
    random_shuffle(a+1,a+K+1);
    random_shuffle(a+1,a+K+1);
    int tmp=cnt;
    ll ans=1ll<<60;
    for(i=0;i<5;i++) {
        cnt=tmp;
        for(j=1;j<=K;j++) if((j>>i)&1) add(S,a[j],0);
        dij();
        for(j=1;j<=K;j++) if(!((j>>i)&1)) ans=min(ans,dis[a[j]]);
        head[S]=0;
        
        cnt=tmp;
        for(j=1;j<=K;j++) if(!((j>>i)&1)) add(S,a[j],0);
        dij();
        for(j=1;j<=K;j++) if((j>>i)&1) ans=min(ans,dis[a[j]]);
        head[S]=0;
    }
    printf("%lld\n",ans);
}
int main() {
    int T;
    scanf("%d",&T);
    while(T--)solve();
}

LOJ#3088. 「GXOI / GZOI2019」旧词

https://loj.ac/problem/3088

题意:

一棵树,每次给出\(x,y\),求\(\sum\limits_{i\le x}\mathrm{dep(lca(i,y))^k}\)

分析:

  • 询问按\(x\)排序,一个一个插。
  • \(k=1\)是个比较经典的问题,插入时\(1\)\(x\)\(+1\),查询时查\(y\)\(1\)的链和即可。
  • 分析本质,加的这个\(1\)就是\(dep_x-dep_{fa_x}\)
  • 很容易把他扩展到\(k​\)任意的情况,每条边的权值就是\(dep_x^K-dep_{fa_x}^K​\)
  • 树剖+线段树搞一搞,时间复杂度\(\mathrm{O(n\log n^2)}​\)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <queue>
using namespace std;
#define N 50050
#define ls p<<1
#define rs p<<1|1
#define mod 998244353
char buf[100000],*p1,*p2;
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd() {
    int x=0; char s=nc();
    while(s<'0') s=nc();
    while(s>='0') x=(((x<<2)+x)<<1)+s-'0',s=nc();
    return x;
}
typedef long long ll;
int head[N],to[N],nxt[N],fa[N],top[N],dep[N],siz[N],son[N],dfn[N],idf[N],cnt;
int n,Q,K;
ll h[N],w[N],sum[N<<2],tag[N<<2],sv[N<<2],ans[N];
struct A {
    int x,y,id;
    bool operator < (const A &u) const {
        return x<u.x;
    }
}q[N];
ll qp(ll x,ll y) {
    ll re=1;for(;y;y>>=1,x=x*x%mod)if(y&1)re=re*x%mod;return re;
}
inline void add(int u,int v) {
    to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
}
void d1(int x) {
    int i;siz[x]=1;
    for(i=head[x];i;i=nxt[i]) {
        dep[to[i]]=dep[x]+1;
        d1(to[i]);siz[x]+=siz[to[i]];
        if(siz[to[i]]>siz[son[x]]) son[x]=to[i];
    }
}
void d2(int x,int t) {
    top[x]=t; int i; dfn[x]=++dfn[0]; idf[dfn[0]]=x;
    if(son[x]) d2(son[x],t);
    for(i=head[x];i;i=nxt[i]) if(to[i]!=son[x]) d2(to[i],to[i]);
}
void build(int l,int r,int p) {
    if(l==r) {sv[p]=w[idf[l]]; return ;}
    int mid=(l+r)>>1;
    build(l,mid,ls),build(mid+1,r,rs);
    sv[p]=(sv[ls]+sv[rs])%mod;
}
inline void giv(int p,ll d) {
    sum[p]=(sum[p]+d*sv[p])%mod; tag[p]+=d;
}
inline void pushdown(int p) {
    if(tag[p]) {
        giv(ls,tag[p]),giv(rs,tag[p]); tag[p]=0;
    }
}
void update(int l,int r,int x,int y,int p) {
    if(x<=l&&y>=r) {giv(p,1); return ;}
    int mid=(l+r)>>1;
    pushdown(p);
    if(x<=mid) update(l,mid,x,y,ls);
    if(y>mid) update(mid+1,r,x,y,rs);
    sum[p]=(sum[ls]+sum[rs])%mod;
}
ll query(int l,int r,int x,int y,int p) {
    if(x<=l&&y>=r) return sum[p];
    int mid=(l+r)>>1; ll re=0;
    pushdown(p);
    if(x<=mid) re=query(l,mid,x,y,ls);
    if(y>mid) re=(re+query(mid+1,r,x,y,rs))%mod;
    return re;
}
void fix(int x) {
    for(;top[x]!=1;x=fa[top[x]]) {
        update(1,n,dfn[top[x]],dfn[x],1);
    }
    update(1,n,1,dfn[x],1);
}
ll ask(int x) {
    ll re=0;
    for(;top[x]!=1;x=fa[top[x]]) {
        re+=query(1,n,dfn[top[x]],dfn[x],1);
    }re+=query(1,n,1,dfn[x],1);
    return re%mod;
}
int main() {
    n=rd(),Q=rd(),K=rd();
    int i;
    for(i=2;i<=n;i++) fa[i]=rd(),add(fa[i],i);
    dep[1]=1,d1(1),d2(1,1);
    for(i=1;i<=n;i++) h[i]=qp(i,K);
    for(i=n;i;i--) h[i]=h[i]-h[i-1];
    for(i=1;i<=n;i++) w[i]=(h[dep[i]]+mod)%mod;
    build(1,n,1);
    for(i=1;i<=Q;i++) {
        q[i].x=rd(),q[i].y=rd(); q[i].id=i;
    }
    sort(q+1,q+Q+1);
    int j=1;
    for(i=1;i<=Q;i++) {
        for(;j<=n&&j<=q[i].x;j++) {
            fix(j);
        }
        ans[q[i].id]=ask(q[i].y);
    }
    for(i=1;i<=Q;i++) printf("%lld\n",(ans[i]+mod)%mod);
}

总结:一场难度低于\(\mathrm{noip}\)的省选,\(d1t3\)的置换部分还是不错的。

posted @ 2019-04-16 16:54 fcwww 阅读(...) 评论(...) 编辑 收藏