【Atcoder】AGC031 C-F简要题解

版权声明:欢迎转载(请附带原链接)ヾ(๑╹◡╹)ノ https://blog.csdn.net/corsica6/article/details/88899608

这一场涉及了很多我不熟悉的技巧,都是神仙题


*C.Differ by 1 Bit

考试的时候想了好久

A B A\to B 的过程等价于 0 A   x o r   B 0\to A\ xor \ B ,考虑如何判断 0 X 0\to X 是否可行

归纳位数 w w 构造:

  • 初始 w = 1 w=1 0 1 0\to 1
  • 对于 w > 1 w>1 ,固定 X X 中为 1 1 的一位 b b ,剩下的 w 1 w-1 位在前 2 w 1 1 2^{w-1}-1 次操作后变成某个数(这个过程中保持 b = 0 b=0 ),下一步 b : = 1 b:=1 ,然后在后 2 w 1 1 2^{w-1}-1 次剩下的 w 1 w-1 位又可以倒序执行前面的操作得到 0 0 。最终可以得到一个只有 b b 这位为1的数。
    考虑归纳这个过程,剩余 w 1 w-1 位最终必然得到了偶数个 1 1
    • 0 1 0 0\to 1\to 0 得到 0 0 1 1 w = 2 w=2 时成立
    • 设已知 w 1 w-1 位时成立,则前 2 w 1 1 2^{w-1}-1 次操作除 b b 位一定能得到奇数个 1 1 ,后 2 w 1 1 2^{w-1}-1 次操作相当于这个有奇数个1的数异或上另一个有奇数个1的数,必然得到偶数个1,成立。。
  • 所以任意 X X 有奇数个 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 ,定义置换运算乘法: t = p q t=pq ,则 t i = p q i t_i=p_{q_i} g = p 1 g=p^{-1} 表示 p p 的逆变换, g p i = i g_{p_i}=i ,存在性质 ( p q ) 1 = q 1 p 1 , p p 1 = ( 1 ) (pq)^{-1}=q^{-1}p^{-1},pp^{-1}=(1)

发现 f ( p , q ) = q p 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}

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 7 = A a 1 A 1 , a 8 = A a 2 A 1 a_7=Aa_1A^{-1},a_8=Aa_2A^{-1} ,归纳得到 a i = A a i 6 A 1 a_i=Aa_{i-6}A^{-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}

所以求出 A i 1 6 A^{\lfloor \frac{i-1}{6}\rfloor} 即可,可以求出轮换直接位移。

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 的限制分居两侧,建S-T最大费用最大流。
二维的情况,显然一个点不可能流出2个单位的流量分别满足 L / R , U / D L/R,U/D 的限制(带费用的流量不可拆分),可以拆出 2 N 2N 个点,分别代表 x , y x,y 坐标,将每个珠宝看做 ( x i , y i , 1 , v i ) (x_i,y_i,1,v_i) 的一条边。但同时满足 L / R , U / D L/R,U/D 的限制还是不可做。

然后就需要关键的一步:

发现 n 80 n\leq 80 不仅可以跑一次费用流,还可以枚举最后选的珠宝数都跑一次费用流!

枚举珠宝数 K K 有奇效:

分别考虑两个维上的所有限制(将选的 K K 个点这一维上坐标按升序排序,假设第 x x 个点坐标大小为 p x p_x ):

  • a i \leq a_i 的最多 b i b_i p b i + 1 &gt; a i \to p_{b_i+1}&gt;a_i
  • a i \geq a_i 的最多 b i b_i p K b i &lt; a i \to p_{K-b_i}&lt;a_i
  • p 1 p 2 . . . p K p_1\leq p_2\leq...\leq p_K

构图:
2 K 2K 个点分别表示对 x x 轴上选的第 i i 个点的限制 p x i ( 1 i K ) px_i(1\leq i\leq K) ,对 y y 轴上选的第 i i 个点的限制 p y i ( 1 i K ) py_i(1\leq i\leq K) ,连边 ( S , p x i , 1 , 0 ) , ( p y i , T , 1 , 0 ) (S,px_i,1,0),(py_i,T,1,0)
再建 2 N 2N 个点分别表示所有珠宝的横纵坐标,连边 ( x i , y i , 1 , v i ) (x_i,y_i,1,v_i)
p x i px_i 向所有限制范围以内的 x i x_i 连流量为 1 1 ,费用为 0 0 的边, y i y_i 向所有可以匹配上的 p y i py_i 连流量为1 ,费用为0的边。

注意只有满流的情况才有资格和答案取 max \max
( Maxflow(S,T)=K \text{Maxflow(S,T)=K} )


*F.Walk on Graph

神仙题

设状态为 ( u , x ) (u,x) ,表示在点 u u 时,值为 x x

考虑倒着走, 走一条边 ( u , v , c ) (u,v,c) 相当于 ( u , x ) ( v , 2 x + c ) (u,x)\to (v,2x+c) ,询问 ( T , 0 ) (T,0) 是否能走到 ( 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) 是双射的(一一对应),所以若存在 ( u , x ) ( v , 2 x + c ) (u,x)\to (v,2x+c) ,那么也存在 ( v , 2 x + c ) ( u , x ) (v,2x+c)\to (u,x)

考虑 f ( x ) = y f(x)=y ,已知 y y ,求解 x x ,则 2 x y c ( m o d M O D ) 2x\equiv y-c\pmod{MOD} 2 2 在奇模数意义下存在逆元所以 x x 的取值是唯一的,同理不同的 x x 对应不同的 f ( x ) f(x) ,所以是一一对应的。

所以不断 x = f ( x ) x=f(x) 一定可以走回 x x ,如果是个奇环,再走一遍就是偶环( u v . . . v u u\to v\to ...\to v\to u )了。

所以 ( u , x ) ( v , 2 x + c ) (u,x)\Leftrightarrow(v,2x+c) 之间可以直接连上边权为 c c 的双向边。

( T , 0 ) (T,0) ( S , R ) (S,R) 在同一个连通块内则有解。

性质2:

( u , x ) (u,x) 沿着它连出的一条边走两次回到自己可以得到 ( u , 4 x + 3 c ) (u,4x+3c) ,如果存在另一条连出的边(可以得到 4 x + 3 c 4x+3c&#x27; ),则存在 ( u , x ) ( u , x + 3 k ( c c ) ) , k Z (u,x)\to (u,x+3k(c-c&#x27;)),k\in Z

4 4 在奇模数意义下存在逆元,所以 ( u , x ) ( u , 4 x + 3 c ) (u,x)\Leftrightarrow(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&#x27;)

4 x 4x 可以表示任何数(对于任意 y y ,都存在 4 x y ( m o d M O D ) 4x\equiv y\pmod{MOD} ),所以 ( u , x ) ( u , x + 3 k ( c c ) ) , k Z (u,x)\to (u,x+3k(c-c&#x27;)),k\in Z

于是可以把有公共点 u u 的边的差值求一个 g c d G u gcdG_u ,对于 u u 来说,距离的循环节变成了 gcd ( 3 G u , M O D ) \gcd(3G_u,MOD) 。考虑 ( u , x ) ( v , 2 x + c ) (u,x)\to (v,2x+c) ,实际上 v v 的循环节是 gcd ( 2 3 G u , gcd ( 3 G v , M O D ) ) \gcd(2·3G_u,\gcd(3G_v,MOD)) ,继续拓展,求出连通图中所有边两两边权之差的 gcd G \gcd G M O D MOD 就变成了 g = gcd ( 3 G , M O D ) g=\gcd(3G,MOD)

性质3:

在模 g g 意义下,所有边权值一样,设为 z z ,可以把所有状态第二位增加 z z ,所有边权 z -z ,运算不变。

( u , x ) ( v , 2 ( x z ) + ( c + z ) + z ) = ( v , 2 x + c ) (u,x&#x27;)\to (v,2(x&#x27;-z)+(c&#x27;+z)+z)=(v,2x&#x27;+c&#x27;)

这样所有边权都是 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 ) ( u , 4 x + 3 c ) = u ( 4 x ) (u,x)\to (u,4x+3c)=u(4x) x x 4 x 4x 等价,所以 p { 0 , 1 } p\in \{0,1\}

最终得到一张有 6 n 6n 个点的图,并查集判断连通性即可

询问相当于判断 ( t , z ) (t,z) 是否能走到 ( s , z + r ) (s,z+r) ,枚举 q q 的取值和 p p 的奇偶性(注意不是具体值),相当于求解是否存在 p p 使得 2 p z + q g = z + r 2^pz+qg=z+r ,预处理一下 2 p z 2^pz 分别在奇偶次幂可以取到的数,判断 z + r q g 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;
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/88899608