这一场涉及了很多我不熟悉的技巧,都是神仙题
考试的时候想了好久
A → B A\to B A→B的过程等价于 0 → A x o r B 0\to A\ xor \ B 0→A xor B,考虑如何判断 0 → X 0\to X 0→X是否可行
归纳位数 w w w构造:
详见代码
#include
#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;
}
抽象代数一脸懵,真是伤脑筋(竟以为自己能找出规律,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
#define fi first
#define se second
#define pii pair
#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();
}
神仙费用流
分析:
一维的情况,可以把每个珠宝看做一个标号为坐标的点,然后按 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 n\leq 80 n≤80不仅可以跑一次费用流,还可以枚举最后选的珠宝数都跑一次费用流!
枚举珠宝数 K K K有奇效:
分别考虑两个维上的所有限制(将选的 K K K个点这一维上坐标按升序排序,假设第 x x x个点坐标大小为 p x p_x px):
构图:
建 2 K 2K 2K个点分别表示对 x x x轴上选的第 i i i个点的限制 p x i ( 1 ≤ i ≤ K ) px_i(1\leq i\leq K) pxi(1≤i≤K),对 y y y轴上选的第 i i i个点的限制 p y i ( 1 ≤ i ≤ K ) py_i(1\leq i\leq 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)
神仙题
设状态为 ( 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}(x\to 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 ) 2x\equiv y-c\pmod{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 u\to v\to ...\to v\to 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')),k\in 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 ) 4x\equiv y\pmod{MOD} 4x≡y(modMOD)),所以 ( u , x ) → ( u , x + 3 k ( c − c ′ ) ) , k ∈ Z (u,x)\to (u,x+3k(c-c')),k\in 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),q\in\{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 } p\in \{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
#include
#include
#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;
}