4.6省选模拟
我太菜了
\(T1\)意识到应该分开处理,但是确实没有想到使用矩阵转移,然后试图暴力推式子无果,就打了打暴力
\(T2\)确实这分治\(+\)最短路太强了....试图在建图上寻找突破点无果,这种题确实是第一次见到,并没有想出来
\(T3\)提答,手玩前两组,感觉高维需要处理一堆东西
这一场打的很差,还是见的少了
\(T1\)
考虑我们要分别输出分子分母,那么必然需要单独维护
计算\(x+\frac{z}{y}=\frac{xy+z}{y}\)
较为显然,\(gcd(xy+z,y)=gcd(y,z)=1,\)那么这个永远不会被约分
那么也就是说,我们上下不需要进行\(gcd\)运算,直接上下单独维护,互相转移就好了
大概明白了
我们现在拥有了当前状态的分子分母,假设新加一个数字
\(\huge\frac{1}{a_0+\frac{1}{a_1+\frac{1}{a+2+...}}}\)
我们现在是\(\frac{a}{b},\)那么新加一个数字\(a_i\)
\(\huge \frac{1}{a_i+\frac{a}{b}}=\frac{1}{\frac{a_ib+a}{b}}=\frac{b}{a_ib+a}\)
那么可以显然的得到转移矩阵(当然我们最后是从右边乘到左边,我们为了从左到右转移,把转移矩阵放到左边就可以区间整体转移了)
那么转移矩阵易得
\(\begin{bmatrix} 0 & 1 \\ 1 & a_i \end{bmatrix}\times\begin{bmatrix} a \\ b \end{bmatrix}=\begin{bmatrix} b \\ a_ib+a \end{bmatrix} \quad\)
那么直接这样直接变转移边取模是没有问题的,反正分子分母的转移形式已经确定并且不会约分,那么直接取模是没有问题的
那么整体转移式子大概就是这个样子,考虑维护出这样一个转移序列就好了
\(\begin{bmatrix} 0 & 1 \\ 1 & a_1 \end{bmatrix}...\begin{bmatrix} 0 & 1 \\ 1 & a_n \end{bmatrix}\times\begin{bmatrix} 1 \\ 0 \end{bmatrix}\)
那么这道题的操作也比较显然需要用矩阵表示一下
那么对于\(W\)操作,\(a_k->a_k+1\)
那么考虑一下我们最后的那个矩阵的变化
\(\begin{bmatrix} 0 & 1 \\ 1 & a_k \end{bmatrix}->\begin{bmatrix} 0 & 1 \\ 1 & a_k+1 \end{bmatrix}\)
那么,构造\(+1\)矩阵
\(\begin{bmatrix} 0 & 1 \\ 1 & a_k \end{bmatrix}\times \begin{bmatrix} 1 & 1 \\ 0 & 1 \end{bmatrix}=\begin{bmatrix} 0 & 1 \\ 1 & a_k+1 \end{bmatrix}\)
对于\(E\)操作
我们有两种情况,\(a_k>1,a_k<=1\)
\(Sit_1:(a_k>1)\)
\(\begin{bmatrix} 0 & 1 \\ 1 & a_k \end{bmatrix}\times \begin{bmatrix} 1 & -1 \\ 0 & 1 \end{bmatrix}=\begin{bmatrix} 0 & 1 \\ 1 & a_k-1 \end{bmatrix}\)
又增加两个,那么相当于在末尾增加两个矩阵
\(\begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}\begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}\)
那么总的可以把三个矩阵乘起来
\(\begin{bmatrix} 1 & -1 \\ 0 & 1 \end{bmatrix}\begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}\begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}=\begin{bmatrix} 0 & -1 \\ 1 & 2 \end{bmatrix}\)
\(Sit_2:(a_k<=1)\)
对于\(a_{k-1}->a_{k-1}+1\)
考虑最后两个矩阵\(k-1,k\)
\(\begin{bmatrix} 0 & 1 \\ 1 & a_{k-1} \end{bmatrix}\begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}\)
那么最后应该变化为
\(\begin{bmatrix} 0 & 1 \\ 1 & a_{k-1} \end{bmatrix}\begin{bmatrix} 1 & 1 \\ 0 & 1 \end{bmatrix}\begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}\)
由于矩阵乘法有结合律,可以先把后两项乘起来
\(\begin{bmatrix} 0 & 1 \\ 1 & a_{k-1} \end{bmatrix}\begin{bmatrix} 1 & 2 \\ 1 & 1 \end{bmatrix}\)
考虑能否直接在原序列最后直接乘上\(Sit_1\)的转移矩阵得到同样效果
\(\begin{bmatrix} 0 & 1 \\ 1 & a_{k-1} \end{bmatrix}\begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}\begin{bmatrix} 0 & -1 \\ 1 & 2 \end{bmatrix}=\begin{bmatrix} 0 & 1 \\ 1 & a_{k=1} \end{bmatrix}\begin{bmatrix} 1 & 2 \\ 1 & 1 \end{bmatrix}\)
目前我们知道了所有的转移矩阵,那么就需要维护翻转.取反,区间乘积的数据结构,那我们祭出平衡树即可
那么就是痛苦的手搓平衡树环节了
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
#define ls son[now][0]
#define rs son[now][1]
#define MAXN 200005
using namespace std;
mt19937 rnd(20050323);
int n,q,son[MAXN][2],siz[MAXN],val[MAXN],rev[MAXN],qf[MAXN];
int sta[MAXN],top;
struct Mat{
int jz[2][2];
void Init()
{
memset(jz,0,sizeof(jz));
}
Mat operator *(const Mat &a)
{
Mat res;
res.Init();
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)
{
for(int k=0;k<2;k++)
{
(res.jz[i][j]+=(jz[i][k]*a.jz[k][j])%mod)%=mod;
}
}
}
return res;
}
}f[4][MAXN],zy[2];
void tupt(int now,int t)
{
f[t][now]=f[t][ls]*zy[sta[now]]*f[t][rs];
}
void swp(int now,int a,int b)
{
swap(f[a][now],f[b][now]);
}
void pushdown(int now)
{
if(rev[now])
{
swap(ls,rs);
swp(now,0,1),swp(now,2,3);
rev[ls]^=1,
rev[rs]^=1;
rev[now]=0;
}
if(qf[now])
{
sta[now]^=1;
swp(now,0,2),swp(now,1,3);
qf[ls]^=1;
qf[rs]^=1;
qf[now]=0;
}
}
void upt(int now)
{
pushdown(ls);
pushdown(rs);
siz[now]=siz[ls]+siz[rs]+1;
tupt(now,0);
swap(ls,rs);
tupt(now,1);
swap(ls,rs);
sta[now]^=1;
tupt(now,2);
swap(ls,rs);
tupt(now,3);
swap(ls,rs);
sta[now]^=1;
}
int newnode(int t)
{
sta[++top]=t;
upt(top);
val[top]=rnd();
return top;
}
int merge(int x, int y)
{
if(!x||!y) return x|y;
pushdown(x);
pushdown(y);
int now;
if(val[x]>val[y])
{
now=x;
rs=merge(rs,y);
}
else
{
now=y;
ls=merge(x,ls);
}
upt(now);
return now;
}
void split(int now,int &x,int &y,int k)
{
if(!now)
{
x=y=0;
return;
}
pushdown(now);
if(k>=siz[ls]+1)
{
x=now;
split(rs,rs,y,k-siz[ls]-1);
}
else
{
y=now;
split(ls,x,ls,k);
}
upt(now);
}
int rt;
void append(int t)
{
rt=merge(rt,newnode(t));
}
void flip(int l,int r)
{
int x,y,z;
split(rt,y,z,r);
split(y,x,y,l-1);
qf[y]^=1;
rt=merge(x,y);
rt=merge(rt,z);
}
void reve(int l,int r)
{
int x,y,z;
split(rt,y,z,r);
split(y,x,y,l-1);
rev[y]^=1;
rt=merge(x,y);
rt=merge(rt,z);
}
void ask()
{
pushdown(rt);
int a=f[0][rt].jz[0][0],b=f[0][rt].jz[1][0];
printf("%lld %lld\n",a,(a+b)%mod);
}
char s[MAXN];
signed main()
{
scanf("%lld%lld",&n,&q);
zy[0].jz[0][0]=zy[0].jz[1][0]=zy[0].jz[1][1]=1;
zy[1].jz[0][0]=2,zy[1].jz[0][1]=1,zy[1].jz[1][0]=mod-1;
scanf("%s",s+1);
for(int i=0;i<4;i++) f[i][0].jz[0][0]=f[i][0].jz[1][1]=1;
for(int i=1;i<=n;i++) append(s[i]=='E');
ask();
for(int i=1,l,r;i<=q;i++)
{
scanf("%s",s);
if(s[0]=='A')
{
scanf("%s",s);
append(s[0]=='E');
}
else scanf("%lld%lld",&l,&r);
if(s[0]=='F') flip(l,r);
else if(s[0]=='R') reve(l, r);
ask();
}
return 0;
}
\(T2\)
确实没有想到,需要把询问离线然后分治时候跑最短路
对于原矩阵进行分割,分割成两个矩阵,然后对于所有的点求一下最短路,然后以直线上的点为必经点更新询问
#include<bits/stdc++.h>
#define INF 2147483647
#define MAXN 100010
#define MAXM 50010
using namespace std;
int n,m,q,ans[MAXN];
template<typename Tp>
void read(Tp &x)
{
x=0;int f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}x*=f;
}
struct Que
{
int id;
int x,y,xx,yy,p1,p2;
}p[MAXN],tmp1[MAXN],tmp2[MAXN],tmp3[MAXN];
priority_queue<pair<int,int> > pq;
int dis[MAXM],hah[MAXM];
int tot,to[MAXM<<1],nxt[MAXM<<1],head[MAXM],val[MAXM<<1];
int id(int x,int y)
{
return (x-1)*m+y;
}
void add(int u,int v,int w)
{
tot++;
to[tot]=v;
val[tot]=w;
nxt[tot]=head[u];
head[u]=tot;
}
void dij(int l1,int r1,int l2,int r2,int x)
{
for(int i=l1;i<=r1;i++)
{
for(int j=l2;j<=r2;j++)
{
int pos=id(i,j);
dis[pos]=INF;
hah[pos]=0;
}
}
dis[x]=0;
pq.push(make_pair(0,x));
while(!pq.empty())
{
int x=pq.top().second;
pq.pop();
if(hah[x]) continue;
hah[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
int xx=(y/m)+(y%m!=0),yy=(y%m==0)?m:y%m;
if(xx<l1||xx>r1||yy<l2||yy>r2) continue;
if(dis[x]+val[i]<dis[y])
{
dis[y]=dis[x]+val[i];
pq.push(make_pair(-dis[y],y));
}
}
}
}
void sol(int l1,int r1,int l2,int r2,int L,int R)
{
if(l1>r1||l2>r2||L>R) return;
int len1=r1-l1+1,len2=r2-l2+1;
if(len1<len2)
{
int mid=(l2+r2)>>1;
for(int i=l1;i<=r1;i++)
{
int pos=id(i,mid);
dij(l1,r1,l2,r2,pos);
for(int j=L;j<=R;j++) ans[p[j].id]=min(ans[p[j].id],dis[p[j].p1]+dis[p[j].p2]);
}
int k1=0,k2=0,k3=0,cnt=L-1;
for(int i=L;i<=R;i++)
{
if(p[i].y<mid&&p[i].yy<mid) tmp1[++k1]=p[i];
else if(p[i].y>mid&&p[i].yy>mid) tmp2[++k2]=p[i];
else tmp3[++k3]=p[i];
}
for(int i=1;i<=k1;i++) p[++cnt]=tmp1[i];
for(int i=1;i<=k3;i++) p[++cnt]=tmp3[i];
for(int i=1;i<=k2;i++) p[++cnt]=tmp2[i];
sol(l1,r1,l2,mid-1,L,L+k1-1);
sol(l1,r1,mid+1,r2,R-k2+1,R);
}
else
{
int mid=(l1+r1)>>1;
for(int i=l2;i<=r2;i++)
{
int pos=id(mid,i);
dij(l1,r1,l2,r2,pos);
for(int j=L;j<=R;j++) ans[p[j].id]=min(ans[p[j].id],dis[p[j].p1]+dis[p[j].p2]);
}
int k1=0,k2=0,k3=0,cnt=L-1;
for(int i=L;i<=R;i++)
{
if(p[i].x<mid&&p[i].xx<mid) tmp1[++k1]=p[i];
else if(p[i].x>mid&&p[i].xx>mid) tmp2[++k2]=p[i];
else tmp3[++k3]=p[i];
}
for(int i=1;i<=k1;i++) p[++cnt]=tmp1[i];
for(int i=1;i<=k3;i++) p[++cnt]=tmp3[i];
for(int i=1;i<=k2;i++) p[++cnt]=tmp2[i];
sol(l1,mid-1,l2,r2,L,L+k1-1);
sol(mid+1,r1,l2,r2,R-k2+1,R);
}
}
int main()
{
read(n),read(m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<m;j++)
{
int u=id(i,j),v=id(i,j+1),w;
read(w);
add(v,u,w),add(u,v,w);
}
}
for(int i=1;i<n;i++)
{
for(int j=1;j<=m;j++)
{
int u=id(i,j),v=id(i+1,j),w;
read(w);
add(u,v,w),add(v,u,w);
}
}
read(q);
for(int i=1;i<=q;i++)
{
read(p[i].x),read(p[i].y),read(p[i].xx),read(p[i].yy);
p[i].p1=id(p[i].x,p[i].y),p[i].p2=id(p[i].xx,p[i].yy);
p[i].id=i,ans[i]=INF;
}
sol(1,n,1,m,1,q);
for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
return 0;
}
\(T3\)
神仙题(作为一个三维的屑属实想不出来高维怎么整)
怎么表示\(n\)维度一个面
二维,面既是线,\(a_1x_1+a_2x_2=b\)
三维,面就是面(废话),\(a_1x_1+a_2x_2+a_3x_3=d\)
拓展到高维公式一致
对于第\(i\)个球,\(|\sum_j a_jx_{i,j}-d|=r\)
枚举正负号高斯消元即可