轻重边
题目描述
解法
可以转化成染色模型,修改就是将路径染上一种新颜色,查询就是问路径上同色相邻点对个数。
直接上树剖即可,时间复杂度 \(O(n\log^2n)\),本题实现的最大难点就是多测。
总结
有区间赋值特性的题目中,可以考虑往染色模型上转化。
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 1000005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int T,n,m,tot,f[M],siz[M],son[M];
int cnt,top[M],num[M],fa[M],dep[M];
struct edge{int v,next;}e[M<<1];
struct node
{
int l,r,c,f;
node(int L=0,int R=0,int C=0,int F=0) :
l(L) , r(R) , c(C) , f(F) {}
node operator + (const node &b) const
{
return node(l,b.r,c+b.c+(r==b.l));
}
}tr[M<<2];
void dfs1(int u,int p)
{
siz[u]=1;fa[u]=p;
dep[u]=dep[p]+1;son[u]=0;
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==p) continue;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;num[u]=++cnt;
if(son[u]) dfs2(son[u],tp);
for(int i=f[u];i;i=e[i].next)
if(e[i].v^fa[u] && e[i].v^son[u])
dfs2(e[i].v,e[i].v);
}
void color(int i,int c,int len)
{
tr[i].f=tr[i].l=tr[i].r=c;tr[i].c=len;
}
void down(int i,int l,int r)
{
if(!tr[i].f) return ;
int mid=(l+r)>>1;
color(i<<1,tr[i].f,mid-l);
color(i<<1|1,tr[i].f,r-mid-1);
tr[i].f=0;
}
void build(int i,int l,int r)
{
tr[i]=node(0,0,0,0);
if(l==r) {tr[i].l=tr[i].r=l;return ;}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
tr[i]=tr[i<<1]+tr[i<<1|1];
}
void cover(int i,int l,int r,int L,int R,int c)
{
if(L>r || l>R) return ;
if(L<=l && r<=R) {color(i,c,r-l);return ;}
int mid=(l+r)>>1;down(i,l,r);
cover(i<<1,l,mid,L,R,c);
cover(i<<1|1,mid+1,r,L,R,c);
tr[i]=tr[i<<1]+tr[i<<1|1];
}
node ask(int i,int l,int r,int L,int R)
{
if(L<=l && r<=R) return tr[i];
int mid=(l+r)>>1;down(i,l,r);
if(mid<L) return ask(i<<1|1,mid+1,r,L,R);
if(R<=mid) return ask(i<<1,l,mid,L,R);
return ask(i<<1,l,mid,L,R)
+ask(i<<1|1,mid+1,r,L,R);
}
void add(int u,int v,int c)
{
while(top[u]^top[v])
{
if(dep[top[u]]<=dep[top[v]]) swap(u,v);
cover(1,1,n,num[top[u]],num[u],c);
u=fa[top[u]];
}
if(dep[u]<=dep[v]) swap(u,v);
cover(1,1,n,num[v],num[u],c);
}
int get(int u,int v)
{
node t1,t2;
while(top[u]^top[v])
{
if(dep[top[u]]>=dep[top[v]])
{
node x=ask(1,1,n,num[top[u]],num[u]);
swap(x.l,x.r);t1=t1+x;
u=fa[top[u]];
}
else
{
t2=ask(1,1,n,num[top[v]],num[v])+t2;
v=fa[top[v]];
}
}
if(dep[u]<=dep[v])
return (t1+ask(1,1,n,num[u],num[v])+t2).c;
node x=ask(1,1,n,num[v],num[u]);swap(x.l,x.r);
return (t1+x+t2).c;
}
void work()
{
n=read();m=read();cnt=tot=0;
for(int i=1;i<=n;i++) f[i]=0;
for(int i=1;i<n;i++)
{
int u=read(),v=read();
e[++tot]=edge{v,f[u]},f[u]=tot;
e[++tot]=edge{u,f[v]},f[v]=tot;
}
dfs1(1,0);dfs2(1,1);build(1,1,n);
for(int i=1;i<=m;i++)
{
int op=read(),x=read(),y=read();
if(op==1) add(x,y,++cnt);
else printf("%d\n",get(x,y));
}
}
signed main()
{
T=read();
while(T--) work();
}
量子通信
题目描述
解法
我更倾向于把本题看成构造,用到的技巧就是:分析特殊数值的含义。
本题最重要的数值就是 \(15(0\leq k\leq 15)\) 和 长度 \(256\),考虑到 \(256=16\times 16\),并且 \(16=15+1\),于是性质就呼之欲出了:如果我们把字典里的串分成长度都为 \(16\) 的 \(16\) 块,那么如果答案为 \(1\),询问串和字典串一定存在相等的块。
那么利用存在块相等的性质来枚举,由于数据随机,单次询问期望判断字典串的个数是 \(\frac{n}{2^{16}}\times 16\approx 100\),判断单个字典串只需要做 \(4\) 次(压缩成 \(\tt ull\) 之后利用 __builtin_popcountll
函数),那么时间复杂度 \(O(m\times 400)\)
感觉完全不需要卡常啊,感觉慢的原因应该在 \(\tt vector\)
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int N = 400005;
typedef unsigned long long ull;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,la,a[N][16];vector<int> v[16][70005];
bool s[N][256];ull a1,a2,b[N][4];char t[70];
ull myRand(ull &k1,ull &k2)
{
ull k3=k1,k4=k2;
k1=k4;k3^=(k3<<23);
k2=k3^k4^(k3>>17)^(k4>>26);
return k2+k4;
}
void gen(int n,ull a1,ull a2)
{
for(int i=1;i<=n;i++)
for(int j=0;j<256;j++)
s[i][j]=(myRand(a1,a2)&(1ull<<32))?1:0;
}
int work()
{
scanf("%s",t);ull z[4]={};
int k=read(),o[16]={},f=la?15:0;
for(int i=0;i<64;i++)
{
int c=0;
if('0'<=t[i] && t[i]<='9') c=t[i]-'0';
if('A'<=t[i] && t[i]<='F') c=t[i]-'A'+10;
c^=f;
o[i/4]=(o[i/4]<<4)|c;
z[i/16]=(z[i/16]<<4)|c;
}
for(int i=0;i<16;i++)
{
for(int &x:v[i][o[i]])
{
int cnt=0;
for(int j=0;j<4;j++)
cnt+=__builtin_popcountll(z[j]^b[x][j]);
if(cnt<=k) return 1;
}
}
return 0;
}
signed main()
{
n=read();m=read();cin>>a1>>a2;
gen(n,a1,a2);
for(int i=1;i<=n;i++)
{
for(int j=0;j<256;j++)
{
a[i][j>>4]=(a[i][j>>4]<<1)|s[i][j];
b[i][j>>6]=(b[i][j>>6]<<1)|s[i][j];
}
for(int j=0;j<16;j++)
v[j][a[i][j]].push_back(i);
}
for(int i=1;i<=m;i++)
printf("%d\n",la=work());
}
密码箱
题目描述
解法
可以把每个 \(a_i\) 都看成作用于 \(\frac{x}{y}\) 的函数,那么 \(f_i(\frac{x}{y})=\frac{a_i\cdot x+y}{x}\)
由于这个函数是线性变换可以写成矩阵,注意这里我们是主动使用矩阵,而不是观察出了什么性质,虽然矩阵并没有简便计算但它是一种易于维护的形式。那么初始是向量 \((1\ \ 0)\),我们按照操作序列从右往左地右乘矩阵:\(\begin{bmatrix}a_i & 1\\1 & 0\end{bmatrix}\)
字符 W
就相当于在末尾添加矩阵 \(\begin{bmatrix}1&1\\1&0\end{bmatrix}\)
至于字符 E
,通过计算可以发现只需要把最后一项减 \(1\),然后再添加两个 \(1\),那么在末尾添加矩阵:
剩下的问题就变成维护操作序列了,我们只需要维护这四种乘积:\(A_nA_{n-1}...A_1/A_1...A_{n-1}A_n/B_nB_{n-1}...B_1/B_1...B_{n-1}B_n\),直接 treap
开写,时间复杂度 \(O(n\log n)\)
总结
遇事不决,矩阵乘法!
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <ctime>
using namespace std;
const int M = 200005;
const int MOD = 998244353;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
void write(int x)
{
if(x>=10) write(x/10);
putchar(x%10+'0');
}
int n,m,rt,cnt,hp[M],fl[M],rv[M],val[M],ls[M],rs[M],siz[M];
struct node {int p[2];node(){p[0]=p[1]=0;}};char s[M];
struct mat
{
int a[4];
mat() {a[0]=a[1]=a[2]=a[3]=0;}
mat operator * (const mat &b) const
{
mat r;
//(0,0) = (0,0) * (0,0) + (0,1) * (1,0)
r.a[0]=(1ll*a[0]*b.a[0]+1ll*a[1]*b.a[2])%MOD;
//(0,1) = (0,0) * (0,1) + (0,1) * (1,1)
r.a[1]=(1ll*a[0]*b.a[1]+1ll*a[1]*b.a[3])%MOD;
//(1,0) = (1,0) * (0,0) + (1,1) * (1,0)
r.a[2]=(1ll*a[2]*b.a[0]+1ll*a[3]*b.a[2])%MOD;
//(1,1) = (1,0) * (0,1) + (1,1) * (1,1)
r.a[3]=(1ll*a[2]*b.a[1]+1ll*a[3]*b.a[3])%MOD;
return r;
}
}W,E,a[M],fa[M],b[M],fb[M];
void up(int x)
{
mat u=(val[x]==0)?W:E;
mat v=(val[x]==0)?E:W;
a[x]=a[rs[x]]*u*a[ls[x]];
b[x]=b[ls[x]]*u*b[rs[x]];
fa[x]=fa[rs[x]]*v*fa[ls[x]];
fb[x]=fb[ls[x]]*v*fb[rs[x]];
siz[x]=siz[ls[x]]+siz[rs[x]]+1;
}
void reve(int x)
{
if(!x) return ;rv[x]^=1;
swap(a[x],b[x]);swap(fa[x],fb[x]);
swap(ls[x],rs[x]);
}
void flip(int x)
{
if(!x) return ;fl[x]^=1;val[x]^=1;
swap(a[x],fa[x]);swap(b[x],fb[x]);
}
void down(int x)
{
if(!x) return ;
if(fl[x]) flip(ls[x]),flip(rs[x]),fl[x]=0;
if(rv[x]) reve(ls[x]),reve(rs[x]),rv[x]=0;
}
node split(int x,int s)
{
node y;
if(!x) return y;
down(x);
if(siz[ls[x]]>=s)
{
y=split(ls[x],s);
ls[x]=y.p[1];y.p[1]=x;
up(x);return y;
}
y=split(rs[x],s-siz[ls[x]]-1);
rs[x]=y.p[0];y.p[0]=x;
up(x);return y;
}
int merge(int x,int y)
{
if(!x || !y) return x+y;
if(hp[x]>=hp[y])
{
down(x);rs[x]=merge(rs[x],y);
up(x);return x;
}
down(y);ls[y]=merge(x,ls[y]);
up(y);return y;
}
void Add(int c)
{
int x=++cnt;hp[x]=rand();val[x]=c;
up(x);rt=merge(rt,x);
}
void Flip(int l,int r)
{
node x=split(rt,l-1);
node y=split(x.p[1],r-l+1);
flip(y.p[0]);
rt=merge(x.p[0],merge(y.p[0],y.p[1]));
}
void Reverse(int l,int r)
{
node x=split(rt,l-1);
node y=split(x.p[1],r-l+1);
reve(y.p[0]);
rt=merge(x.p[0],merge(y.p[0],y.p[1]));
}
void zxy()
{
mat z;z.a[0]=z.a[1]=z.a[3]=1;z=a[rt]*z;
write(z.a[0]);putchar(' ');
write(z.a[1]);puts("");
}
signed main()
{
srand(time(0));
n=read();m=read();scanf("%s",s+1);
a[0].a[0]=a[0].a[3]=1;
b[0]=fa[0]=fb[0]=a[0];
W.a[0]=W.a[1]=W.a[3]=1;
E.a[0]=2;E.a[1]=MOD-1;E.a[2]=1;
for(int i=1;i<=n;i++) Add(s[i]=='E');
zxy();char t[5]={};
for(int i=1;i<=m;i++)
{
scanf("%s",s);
if(s[0]=='A')
{
scanf("%s",t);
Add(t[0]=='E');
}
else
{
int l=read(),r=read();
if(s[0]=='F') Flip(l,r);
if(s[0]=='R') Reverse(l,r);
}
zxy();
}
}