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)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#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)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#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 \mathbb{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)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#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;
}