这一场涉及了很多我不熟悉的技巧,都是神仙题
*C.Differ by 1 Bit
考试的时候想了好久
的过程等价于 ,考虑如何判断 是否可行
归纳位数 构造:
- 初始 ,
- 对于
,固定
中为
的一位
,剩下的
位在前
次操作后变成某个数(这个过程中保持
),下一步
,然后在后
次剩下的
位又可以倒序执行前面的操作得到
。最终可以得到一个只有
这位为1的数。
考虑归纳这个过程,剩余 位最终必然得到了偶数个 :- 得到 个 , 时成立
- 设已知 位时成立,则前 次操作除 位一定能得到奇数个 ,后 次操作相当于这个有奇数个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)
设存在排列 ,定义置换运算乘法: ,则 , 表示 的逆变换, ,存在性质
发现
,强势找规律:
设
,惊奇地发现
,归纳得到
,即
所以求出 即可,可以求出轮换直接位移。
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
神仙费用流
分析:
一维的情况,可以把每个珠宝看做一个标号为坐标的点,然后按
的限制分居两侧,建S-T最大费用最大流。
二维的情况,显然一个点不可能流出2个单位的流量分别满足
的限制(带费用的流量不可拆分),可以拆出
个点,分别代表
坐标,将每个珠宝看做
的一条边。但同时满足
的限制还是不可做。
然后就需要关键的一步:
发现 不仅可以跑一次费用流,还可以枚举最后选的珠宝数都跑一次费用流!
枚举珠宝数 有奇效:
分别考虑两个维上的所有限制(将选的 个点这一维上坐标按升序排序,假设第 个点坐标大小为 ):
- 的最多 个
- 的最多 个
构图:
建
个点分别表示对
轴上选的第
个点的限制
,对
轴上选的第
个点的限制
,连边
再建
个点分别表示所有珠宝的横纵坐标,连边
。
向所有限制范围以内的
连流量为
,费用为
的边,
向所有可以匹配上的
连流量为1 ,费用为0的边。
注意只有满流的情况才有资格和答案取
。
(
)
*F.Walk on Graph
神仙题
设状态为 ,表示在点 时,值为
考虑倒着走, 走一条边 相当于 ,询问 是否能走到 。
性质1:
是双射的(一一对应),所以若存在 ,那么也存在 。
考虑 ,已知 ,求解 ,则 , 在奇模数意义下存在逆元所以 的取值是唯一的,同理不同的 对应不同的 ,所以是一一对应的。
所以不断 一定可以走回 ,如果是个奇环,再走一遍就是偶环( )了。
所以 之间可以直接连上边权为 的双向边。
若 和 在同一个连通块内则有解。
性质2:
沿着它连出的一条边走两次回到自己可以得到 ,如果存在另一条连出的边(可以得到 ),则存在
在奇模数意义下存在逆元,所以 ,
可以表示任何数(对于任意 ,都存在 ),所以
于是可以把有公共点 的边的差值求一个 ,对于 来说,距离的循环节变成了 。考虑 ,实际上 的循环节是 ,继续拓展,求出连通图中所有边两两边权之差的 , 就变成了 。
性质3:
在模 意义下,所有边权值一样,设为 ,可以把所有状态第二位增加 ,所有边权 ,运算不变。
这样所有边权都是 的倍数,转移形如 ,结合之前的变化 , 和 等价,所以 。
最终得到一张有 个点的图,并查集判断连通性即可
询问相当于判断 是否能走到 ,枚举 的取值和 的奇偶性(注意不是具体值),相当于求解是否存在 使得 ,预处理一下 分别在奇偶次幂可以取到的数,判断 能否取到即可。
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;
}