概述
这一场涉及了很多我不熟悉的技巧,都是神仙题
*C.Differ by 1 Bit
考试的时候想了好久
A → B Ato B A→B的过程等价于 0 → A x o r B 0to A xor B 0→A xor B,考虑如何判断 0 → X 0to X 0→X是否可行
归纳位数 w w w构造:
- 初始 w = 1 w=1 w=1, 0 → 1 0to 1 0→1
- 对于
w
>
1
w>1
w>1,固定
X
X
X中为
1
1
1的一位
b
b
b,剩下的
w
−
1
w-1
w−1位在前
2
w
−
1
−
1
2^{w-1}-1
2w−1−1次操作后变成某个数(这个过程中保持
b
=
0
b=0
b=0),下一步
b
:
=
1
b:=1
b:=1,然后在后
2
w
−
1
−
1
2^{w-1}-1
2w−1−1次剩下的
w
−
1
w-1
w−1位又可以倒序执行前面的操作得到
0
0
0。最终可以得到一个只有
b
b
b这位为1的数。
考虑归纳这个过程,剩余 w − 1 w-1 w−1位最终必然得到了偶数个 1 1 1:- 0 → 1 → 0 0to 1to 0 0→1→0得到 0 0 0个 1 1 1, w = 2 w=2 w=2时成立
- 设已知 w − 1 w-1 w−1位时成立,则前 2 w − 1 − 1 2^{w-1}-1 2w−1−1次操作除 b b b位一定能得到奇数个 1 1 1,后 2 w − 1 − 1 2^{w-1}-1 2w−1−1次操作相当于这个有奇数个1的数异或上另一个有奇数个1的数,必然得到偶数个1,成立。。
- 所以任意 X X X有奇数个 1 1 1的情况都可以递归构造出解。
详见代码
#include<bits/stdc++.h>
#define RI register
using namespace std;
const int N=(1<<17)+10,mod=1e9+7;
typedef long long ll;
int n,A,B,bs;
int bel[N],tim;
int ans[N],cnt,S;
bool vs[N];
inline int cont(int x)
{
int re=0,i;
for(i=0;i<n;++i) if((x>>i)&1) re++;
return re;
}
vector<int> cal(int t,int x)
{
vector<int>re;re.resize((1<<t));
if(t==1){re[1]=1;return re;}
int pos,i;
for(i=0;i<t;++i) if((x>>i)&1) {pos=i;break;}
vector<int>nw;
nw=cal(t-1,1);
for(i=0;i<(1<<(t-1));++i) re[i]=(((nw[i]>>pos)<<(pos+1))|(nw[i]&((1<<pos)-1)));
nw=cal(t-1,(((x>>(pos+1))<<pos)|(x&((1<<pos)-1)))^1);
for(i=0;i<(1<<(t-1));++i){
if(pos>0) re[i+(1<<(t-1))]=1^((1<<pos)|((nw[i]>>(pos))<<(pos+1))|(nw[i]&((1<<pos)-1)));
else re[i+(1<<(t-1))]=1|((nw[i]^1)<<1);
}
return re;
}
int main(){
int i,j,x,y;
scanf("%d%d%d",&n,&A,&B);
S=(1<<n);bs=A;B^=A;
if(cont(B)%2==0) {puts("NO");return 0;}
puts("YES");
vector<int> as=cal(n,B);
for(i=0;i<S;++i) printf("%d ",as[i]^A);
return 0;
}
**D.A Sequence of Permutations
抽象代数一脸懵,真是伤脑筋(竟以为自己能找出规律,too young too naive)
设存在排列 p , q , t p,q,t p,q,t,定义置换运算乘法: t = p q t=pq t=pq,则 t i = p q i t_i=p_{q_i} ti=pqi, g = p − 1 g=p^{-1} g=p−1表示 p p p的逆变换, g p i = i g_{p_i}=i gpi=i,存在性质 ( p q ) − 1 = q − 1 p − 1 , p p − 1 = ( 1 ) (pq)^{-1}=q^{-1}p^{-1},pp^{-1}=(1) (pq)−1=q−1p−1,pp−1=(1)
发现
f
(
p
,
q
)
=
q
p
−
1
f(p,q)=qp^{-1}
f(p,q)=qp−1,强势找规律:
a
1
=
p
a
2
=
q
a
3
=
q
p
−
1
a
4
=
q
p
−
1
q
−
1
a
5
=
q
p
−
1
q
−
1
p
q
−
1
a
6
=
q
p
−
1
q
−
1
p
2
q
−
1
a
7
=
q
p
−
1
q
−
1
p
q
p
q
−
1
a
8
=
q
p
−
1
q
−
1
p
q
p
−
1
q
p
q
−
1
a_1=p\ a_2=q\ a_3=qp^{-1}\ a_4=qp^{-1}q^{-1}\ a_5=qp^{-1}q^{-1}pq^{-1}\ a_6=qp^{-1}q^{-1}p^2q^{-1}\ a_7=qp^{-1}q^{-1}pqpq^{-1}\ a_8=qp^{-1}q^{-1}pqp^{-1}qpq^{-1}
a1=pa2=qa3=qp−1a4=qp−1q−1a5=qp−1q−1pq−1a6=qp−1q−1p2q−1a7=qp−1q−1pqpq−1a8=qp−1q−1pqp−1qpq−1
设
A
=
q
p
−
1
q
−
1
p
(
A
−
1
=
p
−
1
q
p
q
−
1
)
A=qp^{-1}q^{-1}p(A^{-1}=p^{-1}qpq^{-1})
A=qp−1q−1p(A−1=p−1qpq−1),惊奇地发现
a
7
=
A
a
1
A
−
1
,
a
8
=
A
a
2
A
−
1
a_7=Aa_1A^{-1},a_8=Aa_2A^{-1}
a7=Aa1A−1,a8=Aa2A−1,归纳得到
a
i
=
A
a
i
−
6
A
−
1
a_i=Aa_{i-6}A^{-1}
ai=Aai−6A−1,即
a
i
=
A
.
.
.
A
a
(
i
−
1
)
%
6
+
1
A
−
1
.
.
.
A
−
1
a_i=A...Aa_{(i-1)%6+1}A^{-1}...A^{-1}
ai=A...Aa(i−1)%6+1A−1...A−1
所以求出 A ⌊ i − 1 6 ⌋ A^{lfloor frac{i-1}{6}rfloor} A⌊6i−1⌋即可,可以求出轮换直接位移。
code from sigongzi
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define space putchar(' ')
#define enter putchar('n')
#define MAXN 100005
#define eps 1e-10
//#define ivorysi
using namespace std;
typedef long long int64;
typedef unsigned int u32;
typedef double db;
template<class T>
void read(T &res) {
res = 0;T f = 1;char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
}
int N,K;
struct pl {
int f[100005];
friend pl operator * (const pl &a,const pl &b) {
pl c;
for(int i = 1 ; i <= N ; ++i) c.f[i] = a.f[b.f[i]];
return c;
}
pl inv() {
pl c;
for(int i = 1 ; i <= N ; ++i) {
c.f[f[i]] = i;
}
return c;
}
}p,q,iq,ip,s,res,t,ans;
void fpow(int c) {
for(int i = 1 ; i <= N ; ++i) res.f[i] = i;
t = s;
while(c) {
if(c & 1) res = res * t;
t = t * t;
c >>= 1;
}
}
void Solve() {
read(N);read(K);
for(int i = 1 ; i <= N ; ++i) read(p.f[i]);
for(int i = 1 ; i <= N ; ++i) read(q.f[i]);
ip = p.inv();iq = q.inv();
s = q * ip * iq * p;
fpow((K - 1) / 6);
int x = (K - 1) % 6;
if(x == 0) {
ans = res * p * res.inv();
}
else if(x == 1) {
ans = res * q * res.inv();
}
else if(x == 2) {
ans = res * q * ip * res.inv();
}
else if(x == 3) {
res = res * q;
ans = res * ip * res.inv();
}
else if(x == 4) {
res = res * q * ip;
ans = res * iq * res.inv();
}
else if(x == 5) {
res = res * q * ip;
ans = res * iq * p * res.inv();
}
for(int i = 1 ; i <= N ; ++i) {
out(ans.f[i]);space;
}
enter;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
}
**E.Snuke the Phantom Thief
神仙费用流
分析:
一维的情况,可以把每个珠宝看做一个标号为坐标的点,然后按
L
/
R
L/R
L/R的限制分居两侧,建S-T最大费用最大流。
二维的情况,显然一个点不可能流出2个单位的流量分别满足
L
/
R
,
U
/
D
L/R,U/D
L/R,U/D的限制(带费用的流量不可拆分),可以拆出
2
N
2N
2N个点,分别代表
x
,
y
x,y
x,y坐标,将每个珠宝看做
(
x
i
,
y
i
,
1
,
v
i
)
(x_i,y_i,1,v_i)
(xi,yi,1,vi)的一条边。但同时满足
L
/
R
,
U
/
D
L/R,U/D
L/R,U/D的限制还是不可做。
然后就需要关键的一步:
发现 n ≤ 80 nleq 80 n≤80不仅可以跑一次费用流,还可以枚举最后选的珠宝数都跑一次费用流!
枚举珠宝数 K K K有奇效:
分别考虑两个维上的所有限制(将选的 K K K个点这一维上坐标按升序排序,假设第 x x x个点坐标大小为 p x p_x px):
- ≤ a i leq a_i ≤ai的最多 b i b_i bi个 → p b i + 1 > a i to p_{b_i+1}>a_i →pbi+1>ai
- ≥ a i geq a_i ≥ai的最多 b i b_i bi个 → p K − b i < a i to p_{K-b_i}<a_i →pK−bi<ai
- p 1 ≤ p 2 ≤ . . . ≤ p K p_1leq p_2leq...leq p_K p1≤p2≤...≤pK
构图:
建
2
K
2K
2K个点分别表示对
x
x
x轴上选的第
i
i
i个点的限制
p
x
i
(
1
≤
i
≤
K
)
px_i(1leq ileq K)
pxi(1≤i≤K),对
y
y
y轴上选的第
i
i
i个点的限制
p
y
i
(
1
≤
i
≤
K
)
py_i(1leq ileq K)
pyi(1≤i≤K),连边
(
S
,
p
x
i
,
1
,
0
)
,
(
p
y
i
,
T
,
1
,
0
)
(S,px_i,1,0),(py_i,T,1,0)
(S,pxi,1,0),(pyi,T,1,0)
再建
2
N
2N
2N个点分别表示所有珠宝的横纵坐标,连边
(
x
i
,
y
i
,
1
,
v
i
)
(x_i,y_i,1,v_i)
(xi,yi,1,vi)。
p
x
i
px_i
pxi向所有限制范围以内的
x
i
x_i
xi连流量为
1
1
1,费用为
0
0
0的边,
y
i
y_i
yi向所有可以匹配上的
p
y
i
py_i
pyi连流量为1 ,费用为0的边。
注意只有满流的情况才有资格和答案取
max
max
max。
(
Maxflow(S,T)=K
text{Maxflow(S,T)=K}
Maxflow(S,T)=K)
*F.Walk on Graph
神仙题
设状态为 ( u , x ) (u,x) (u,x),表示在点 u u u时,值为 x x x
考虑倒着走, 走一条边 ( u , v , c ) (u,v,c) (u,v,c)相当于 ( u , x ) → ( v , 2 x + c ) (u,x)to (v,2x+c) (u,x)→(v,2x+c),询问 ( T , 0 ) (T,0) (T,0)是否能走到 ( S , R ) (S,R) (S,R)。
性质1:
f ( x ) = 2 x + c ( m o d M O D ) ( x → 2 x + c ) f(x)=2x+c pmod {MOD}(xto 2x+c) f(x)=2x+c(modMOD)(x→2x+c)是双射的(一一对应),所以若存在 ( u , x ) → ( v , 2 x + c ) (u,x)to (v,2x+c) (u,x)→(v,2x+c),那么也存在 ( v , 2 x + c ) → ( u , x ) (v,2x+c)to (u,x) (v,2x+c)→(u,x)。
考虑 f ( x ) = y f(x)=y f(x)=y,已知 y y y,求解 x x x,则 2 x ≡ y − c ( m o d M O D ) 2xequiv y-cpmod{MOD} 2x≡y−c(modMOD), 2 2 2在奇模数意义下存在逆元所以 x x x的取值是唯一的,同理不同的 x x x对应不同的 f ( x ) f(x) f(x),所以是一一对应的。
所以不断 x = f ( x ) x=f(x) x=f(x)一定可以走回 x x x,如果是个奇环,再走一遍就是偶环( u → v → . . . → v → u uto vto ...to vto u u→v→...→v→u)了。
所以 ( u , x ) ⇔ ( v , 2 x + c ) (u,x)Leftrightarrow(v,2x+c) (u,x)⇔(v,2x+c)之间可以直接连上边权为 c c c的双向边。
若 ( T , 0 ) (T,0) (T,0)和 ( S , R ) (S,R) (S,R)在同一个连通块内则有解。
性质2:
( u , x ) (u,x) (u,x)沿着它连出的一条边走两次回到自己可以得到 ( u , 4 x + 3 c ) (u,4x+3c) (u,4x+3c),如果存在另一条连出的边(可以得到 4 x + 3 c ′ 4x+3c' 4x+3c′),则存在 ( u , x ) → ( u , x + 3 k ( c − c ′ ) ) , k ∈ Z (u,x)to (u,x+3k(c-c')),kin Z (u,x)→(u,x+3k(c−c′)),k∈Z
4 4 4在奇模数意义下存在逆元,所以 ( u , x ) ⇔ ( u , 4 x + 3 c ) (u,x)Leftrightarrow(u,4x+3c) (u,x)⇔(u,4x+3c),
( u , 4 x + 3 c ) → ( u , x ) → ( u , 4 x + 3 c ′ ) (u,4x+3c)to (u,x)to (u,4x+3c') (u,4x+3c)→(u,x)→(u,4x+3c′)
4 x 4x 4x可以表示任何数(对于任意 y y y,都存在 4 x ≡ y ( m o d M O D ) 4xequiv ypmod{MOD} 4x≡y(modMOD)),所以 ( u , x ) → ( u , x + 3 k ( c − c ′ ) ) , k ∈ Z (u,x)to (u,x+3k(c-c')),kin Z (u,x)→(u,x+3k(c−c′)),k∈Z
于是可以把有公共点 u u u的边的差值求一个 g c d G u gcdG_u gcdGu,对于 u u u来说,距离的循环节变成了 gcd ( 3 G u , M O D ) gcd(3G_u,MOD) gcd(3Gu,MOD)。考虑 ( u , x ) → ( v , 2 x + c ) (u,x)to (v,2x+c) (u,x)→(v,2x+c),实际上 v v v的循环节是 gcd ( 2 ⋅ 3 G u , gcd ( 3 G v , M O D ) ) gcd(2·3G_u,gcd(3G_v,MOD)) gcd(2⋅3Gu,gcd(3Gv,MOD)),继续拓展,求出连通图中所有边两两边权之差的 gcd G gcd G gcdG, M O D MOD MOD就变成了 g = gcd ( 3 G , M O D ) g=gcd(3G,MOD) g=gcd(3G,MOD)。
性质3:
在模 g g g意义下,所有边权值一样,设为 z z z,可以把所有状态第二位增加 z z z,所有边权 − z -z −z,运算不变。
( u , x ′ ) → ( v , 2 ( x ′ − z ) + ( c ′ + z ) + z ) = ( v , 2 x ′ + c ′ ) (u,x')to (v,2(x'-z)+(c'+z)+z)=(v,2x'+c') (u,x′)→(v,2(x′−z)+(c′+z)+z)=(v,2x′+c′)
这样所有边权都是 g g g的倍数,转移形如 ( u , x ) → ( v , 2 p x + q g ) , q ∈ { 0 , 1 , 2 } (u,x)to(v,2^px+qg),qin{0,1,2} (u,x)→(v,2px+qg),q∈{0,1,2},结合之前的变化 ( u , x ) → ( u , 4 x + 3 c ) = u ( 4 x ) (u,x)to (u,4x+3c)=u(4x) (u,x)→(u,4x+3c)=u(4x), x x x和 4 x 4x 4x等价,所以 p ∈ { 0 , 1 } pin {0,1} p∈{0,1}。
最终得到一张有 6 n 6n 6n个点的图,并查集判断连通性即可
询问相当于判断 ( t , z ) (t,z) (t,z)是否能走到 ( s , z + r ) (s,z+r) (s,z+r),枚举 q q q的取值和 p p p的奇偶性(注意不是具体值),相当于求解是否存在 p p p使得 2 p z + q g = z + r 2^pz+qg=z+r 2pz+qg=z+r,预处理一下 2 p z 2^pz 2pz分别在奇偶次幂可以取到的数,判断 z + r − q g z+r-qg z+r−qg能否取到即可。
code from alan_cty
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e6+5;
int n,m,q,mod,s,t,r,u[N],v[N],c[N],g,fa[N];
bool can[2][N];
int Id(int x,int y,int z) {return (x-1)*6+y*3+z;}
int get(int x) {return fa[x]==x?x:fa[x]=get(fa[x]);}
void link(int x,int y) {
x=get(x);y=get(y);
if (x==y) return;
fa[x]=y;
}
int main() {
scanf("%d%d%d%d",&n,&m,&q,&mod);
fo(i,1,m) {
scanf("%d%d%d",&u[i],&v[i],&c[i]);
g=__gcd(g,abs(c[i]-c[1]));
}
if (!g) g=mod;mod=__gcd(mod,3*g);int z=c[1]%g;
fo(i,0,6*n) fa[i]=i;
fo(i,1,m) {
int w=(c[i]-z)/g;
fo(p,0,1)
fo(q,0,2) {
link(Id(u[i],p,q),Id(v[i],1-p,(2*q+w)%3));
link(Id(v[i],p,q),Id(u[i],1-p,(2*q+w)%3));
}
}
for(int i=0,j=z;i<mod<<1;i++,(j<<=1)%=mod) can[i&1][j]=1;
for(;q;q--) {
scanf("%d%d%d",&s,&t,&r);
int ret=0;
fo(p,0,1) fo(q,0,2) if (get(Id(t,0,0))==get(Id(s,p,q))) ret|=can[p][(r+z+(3-q)*g)%mod];
puts(ret?"YES":"NO");
}
return 0;
}
最后
以上就是甜美哈密瓜为你收集整理的【Atcoder】AGC031 C-F简要题解的全部内容,希望文章能够帮你解决【Atcoder】AGC031 C-F简要题解所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复