AtCoder Grand Contest 031

链接

D. A Sequence of Permutations

考虑 \(f(p,q)=pq^{-1}\)。枚举一下有:

\[p\\ q\\ qp^-\\ qp^-q^-\\ qp^-q^-pq^-\\ qp^-q^-ppq^-\\ qp^-q^-pqpq^-\\ qp^-q^-pqp^-qpq^- \]

肉眼可见有 \(qp^-q^-p\) 一直保留,所以本质上是一个 \(6\) 的循环节。

那么直接快速幂即可。

复杂度 \(O(n\log k)\)

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100010
using namespace std;
struct perm{
    int a[N];
    perm(){memset(a,0,sizeof(a));}
    int& operator [](int x){return a[x];}
};
int n,k;
perm inv(perm &a){perm b;for(int i=1;i<=n;i++) b[a[i]]=i;return b;}
perm r,c;
perm operator *(perm a,perm b){for(int i=1;i<=n;i++) c[i]=a[b[i]];return c;}
perm ksm(perm a,int b)
{
    for(int i=1;i<=n;i++) r[i]=i;
    for(;b;b>>=1,a=a*a) if(b&1) r=r*a;
    return r;
}
perm p,q,t,ans;
int main()
{
    scanf("%d%d",&n,&k);--k;
    for(int i=1;i<=n;i++) scanf("%d",&p[i]);
    for(int i=1;i<=n;i++) scanf("%d",&q[i]);
    t=q*inv(p)*inv(q)*p;
    ans=p;
    for(int i=1;i<k%6;i++) ans=q,q=q*inv(p),p=ans;
    ans=ksm(t,k/6)*ans*ksm(inv(t),k/6);
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    return 0;
}

E. Snuke the Phantom Thief

考虑行列分开算,可以发现一个 \(\leq a_i\) 个数不超过 \(b_i\) 限制,可以看做第 \(b_i+1\) 个宝石必须 \(>a_i\)。假设总共拿了 \(k\) 颗宝石,那么一个 \(\geq a_i\) 个数不超过 \(b_i\) 限制可以看做第 \(b_{k-i}\) 个宝石必须 \(<a_i\)

枚举 \(k\),那么每个宝石等价于有一个 rank 的限制,排名必须在 \([l,r]\) 区间。

然后行列建点,行可行区间向宝石连边,宝石向列可行区间连边,每个宝石独自限流 \(1\),跑最大费用最大流即可。

复杂度 \(O(n^3m)\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef __int128 lll;
const int N=30010,M=400010,inf=1000000000;
const ll winf=1000000000000000000ll;
struct road{
    int nxt,to,f;
    ll w;
}r[M<<1];
int head[N],cnt=1;
void add(int u,int v,int f,ll w)
{
    r[++cnt]=(road){head[u],v,f,w};head[u]=cnt;
    r[++cnt]=(road){head[v],u,0,-w};head[v]=cnt;
}
namespace MCMF{
    ll dis[N];
    int all,fl[N],pre[N],bef[N];
    bool in[N];
    queue<int>q;
    bool spfa(int s,int t)
    {
        for(int i=1;i<=all;i++) dis[i]=-winf,fl[i]=inf,in[i]=false;
        dis[s]=0; pre[t]=-1;in[s]=true;
        q.push(s);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            in[u]=false;
            for(int i=head[u];i;i=r[i].nxt)
            if(r[i].f)
            {
                int v=r[i].to;
                if(dis[v]<dis[u]+r[i].w)
                {
                    dis[v]=dis[u]+r[i].w;
                    fl[v]=min(fl[u],r[i].f);
                    pre[v]=u,bef[v]=i;
                    if(!in[v]) in[v]=true,q.push(v);
                }
            }
        }
        return pre[t]!=-1;
    }
    int maxf;ll minw;
    void work(int s,int t)
    {
        while(spfa(s,t))
        {
            maxf+=fl[t],minw+=fl[t]*dis[t];
            for(int u=t;u!=s;u=pre[u])
            r[bef[u]].f-=fl[t],r[bef[u]^1].f+=fl[t];
        }
    }
    void clear()
    {
        for(int i=1;i<=all;i++) head[i]=dis[i]=0;
        all=0;cnt=1;
        maxf=minw=0;
    }
}
inline void chkmin(int &x,int y){x=min(x,y);}
inline void chkmax(int &x,int y){x=max(x,y);}
int x[N],y[N];ll w[N];
int px[N],py[N],po[N];
int s,t,tt,n,m;
int lx[N],rx[N],ly[N],ry[N];
ll ans=0;
void solve(int k)
{
    auto id=[&](int x,int d){return x+2*k+d*n;};
    s=id(n,1)+1,t=s+1;MCMF::all=t;
    for(int i=1;i<=k;i++) add(s,i,1,0),add(i+k,t,1,0);
    for(int i=1;i<=n;i++) add(id(i,0),id(i,1),1,w[i]),lx[i]=ly[i]=1,rx[i]=ry[i]=100;
    for(int i=1;i<=m;i++)
    if(po[i]==0 && py[i]+1<=k) chkmax(lx[py[i]+1],px[i]+1);
    else if(po[i]==1 && py[i]+1<=k) chkmin(rx[k-py[i]],px[i]-1);
    else if(po[i]==2 && py[i]+1<=k) chkmax(ly[py[i]+1],px[i]+1);
    else if(po[i]==3 && py[i]+1<=k) chkmin(ry[k-py[i]],px[i]-1);
    for(int i=2;i<=k;i++) chkmax(lx[i],lx[i-1]),chkmax(ly[i],ly[i-1]);
    for(int i=k-1;i;i--) chkmin(rx[i],rx[i+1]),chkmin(ry[i],ry[i+1]);
    for(int i=1;i<=k;i++) for(int j=1;j<=n;j++) if(lx[i]<=x[j] && x[j]<=rx[i]) add(i,id(j,0),1,0);
    for(int i=1;i<=k;i++) for(int j=1;j<=n;j++) if(ly[i]<=y[j] && y[j]<=ry[i]) add(id(j,1),i+k,1,0);
    MCMF::work(s,t);
    if(MCMF::maxf==k) ans=max(ans,MCMF::minw);
    MCMF::clear();
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d%d%lld",&x[i],&y[i],&w[i]);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        char op[3];
        scanf("%s%d%d",op,&px[i],&py[i]);
        po[i]=(op[0]=='L'?0:(op[0]=='R'?1:(op[0]=='D'?2:3)));
    }
    for(int i=1;i<=n;i++) solve(i);
    printf("%lld\n",ans);
    return 0;
}

F. Walk on Graph

神仙题。

首先可以发现如果一个点周围有边 \(a\),那么来回走就能 \(x\rightarrow 4x+3a\)。如果同时有 \(x\rightarrow 4x+3b\),由于 \(4\) 在奇数模数下有逆元,所以有 \(x\rightarrow x+(a-b)\)

不妨令 \(g=\gcd\{\Delta w\}\),那么 \(x\) 可以任意加上 \(3qg,q\in \Z\)。这样可以令 \(\text{mod}'=\gcd(\text{mod},3g)\)

考虑将每个数字的状态用 \((p,q)=px+qg\) 表示,其中 \(p\)\(2\) 的次幂。可以发现 \(q\in[0,2]\),然后显然有 \((px,qg)\rightarrow (4px,4qg)=(4px,qg)\),所以 \(p\in[1,2]\)

这样总共只有 \(6\) 种状态,总共 \(6n\) 个点,直接枚举每条边对应点连边即可。

考虑答案,可以发现最后要从状态 \((p,q)\) 走到 \((0,0)\),而且 \((p,q)\) 需要能表示 \(r\),这个可以直接预处理,复杂度 \(O(\text{mod})\)

总复杂度 \(O(\text{mod}+(n+m+q)\log n)\)

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 600010
using namespace std;
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
int x[N],y[N],w[N],f[N];
int find(int x){return x==f[x]?f[x]:(f[x]=find(f[x]));}
void merge(int x,int y){x=find(x);y=find(y);if(x!=y) f[x]=y;}
bool pre[2][1000010];
int main()
{
    int n,m,q,mod,g=0;
    scanf("%d%d%d%d",&n,&m,&q,&mod);
    for(int i=1;i<=m;i++) scanf("%d%d%d",&x[i],&y[i],&w[i]);
    for(int i=2;i<=m;i++) g=gcd(g,abs(w[i]-w[1]));
    if(!g) g=mod;
    else mod=gcd(mod,g*3);
    int w0=w[1]%g;
    auto id=[&](int u,int p,int q){return (u-1)*6+p*3+q;};
    for(int i=0,j=w0;i<mod*2;i++,j=j*2%mod) pre[i&1][j]=true;
    for(int i=1;i<=6*n;i++) f[i]=i;
    for(int i=1;i<=m;i++)
        for(int p=0;p<2;p++)
            for(int q=0;q<3;q++)
            merge(id(x[i],p,q),id(y[i],!p,(2*q+(w[i]-w0)/g)%3)),
            merge(id(y[i],p,q),id(x[i],!p,(2*q+(w[i]-w0)/g)%3));
    auto check=[&](int s,int t,int l)
    {
        for(int p=0;p<2;p++)
            for(int q=0;q<3;q++)
            if(find(id(s,p,q))==find(id(t,0,0)) && pre[p][(l+w0+(3-q)*g)%mod]) return true;
        return false;
    };
    while(q --> 0)
    {
        int s,t,l;scanf("%d%d%d",&s,&t,&l);
        puts(check(s,t,l)?"YES":"NO");
    }
    return 0;
}
posted @ 2023-09-20 18:55  Flying2018  阅读(11)  评论(0)    收藏  举报