NOI2021部分题目题解
D1T1
很一眼不能直接LCT维护。
考虑树剖。先看修改。
当我们跳上一个重链时,重链当前点下的点的边权要置为0,然后整段覆盖1和打上时间标记。
在我们查询的时候,重链内部的点肯定是没有问题的,重链顶的点暴力查询时间和链顶上面一个点的时间标记看是不是要计算入答案。
时间复杂度\(O(nlog^2n)\),需要写两颗线段树维护。
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 100000
#define W (1<<18)
#define mod 1000000007
#define eps (1e-5)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
using namespace std;
I void read(int &x){char s=Gc();x=0;while(s<'0'||s>'9') s=Gc();while(s>='0'&&s<='9') x=x*10+s-48,s=Gc();}
int n,m,k,T,lcas,op,x,y,fa[N+5],siz[N+5],top[N+5],son[N+5],d[N+5],id[N+5],idea,Sum[N+5<<2],Fl[N+5<<2];
struct yyy{int to,z;};
struct ljb{int head,h[N+5];yyy f[N+5<<1];I void add(int x,int y){f[++head]=(yyy){y,h[x]};h[x]=head;}}s;
I void dfs1(int x,int last){
yyy tmp;fa[x]=last;d[x]=d[last]+1;siz[x]=1;for(int i=s.h[x];i;i=tmp.z) tmp=s.f[i],tmp.to^last&&(dfs1(tmp.to,x),siz[x]+=siz[tmp.to],siz[son[x]]<siz[tmp.to]&&(son[x]=tmp.to));
}
I void dfs2(int x,int last){
yyy tmp;top[x]=last;id[x]=++idea;if(!son[x]) return;dfs2(son[x],last);for(int i=s.h[x];i;i=tmp.z) tmp=s.f[i],tmp.to^fa[x]&&tmp.to^son[x]&&(dfs2(tmp.to,tmp.to),0);
}
I void swap(int &x,int &y){x^=y^=x^=y;}
I int lca(int x,int y){while(top[x]^top[y]) d[top[x]]<d[top[y]]&&(swap(x,y),0),x=fa[top[x]];return d[x]<d[y]?x:y;}
struct Time_Tree{
int F[N+5<<2];I void clear(){Me(F,0);}
I void push(int now){F[now]&&(F[now<<1]=F[now<<1|1]=F[now],F[now]=0);}
I void get(int x,int y,int z,int l=1,int r=n,int now=1){
if(x<=l&&r<=y) {F[now]=z;return;}int m=l+r>>1;push(now);x<=m&&(get(x,y,z,l,m,now<<1),0);y>m&&(get(x,y,z,m+1,r,now<<1|1),0);
}
I int find(int x,int l=1,int r=n,int now=1){if(l==r) return F[now];int m=l+r>>1;push(now);return x<=m?find(x,l,m,now<<1):find(x,m+1,r,now<<1|1);}
}S1;
struct Sum_Tree{
int Sum[N<<2],F[N+5<<2];I void clear(){Me(Sum,0);Me(F,-1);}
I void pushF(int l,int r,int now){F[now]=F[now>>1];Sum[now]=F[now>>1]*(r-l+1);}
I void push(int l,int r,int now){int m=l+r>>1;~F[now]&&(pushF(l,m,now<<1),pushF(m+1,r,now<<1|1),F[now]=-1);}
I void Up(int now){Sum[now]=Sum[now<<1]+Sum[now<<1|1];}
I void get(int x,int y,int z,int l=1,int r=n,int now=1){
if(x<=l&&r<=y){F[now]=z;Sum[now]=z*(r-l+1);return;}int m=l+r>>1;push(l,r,now);x<=m&&(get(x,y,z,l,m,now<<1),0);y>m&&(get(x,y,z,m+1,r,now<<1|1),0);Up(now);
}
I int find(int x,int y,int l=1,int r=n,int now=1){
if(x<=l&&r<=y) return Sum[now];int m=l+r>>1,Ans=0;push(l,r,now);x<=m&&(Ans+=find(x,y,l,m,now<<1));y>m&&(Ans+=find(x,y,m+1,r,now<<1|1));return Ans;
}
}S2;
I void change(int x,int y,int ti){
lcas=lca(x,y);lcas^1&&(S2.get(id[lcas],id[lcas],0),0);while(top[x]^top[y]){
d[top[x]]<d[top[y]]&&(swap(x,y),0);son[x]&&(S2.get(id[x]+1,id[x]+1,0),0);S1.get(id[top[x]],id[x],ti);S2.get(id[top[x]],id[x],1);x=fa[top[x]];
}d[x]>d[y]&&(swap(x,y),0);son[y]&&(S2.get(id[y]+1,id[y]+1,0),0);S1.get(id[x],id[y],ti);x^y&&(S2.get(id[x]+1,id[y],1),0);
}
I void GetAns(int x,int y,int ti){
int Ans=0;while(top[x]^top[y]){
d[top[x]]<d[top[y]]&&(swap(x,y),0);Ans+=S2.find(id[top[x]],id[x]);S1.find(id[top[x]])<S1.find(id[fa[top[x]]])&&(Ans-=S2.find(id[top[x]],id[top[x]]),0);x=fa[top[x]];
}d[x]>d[y]&&(swap(x,y),0);x^y&&(Ans+=S2.find(id[x]+1,id[y]));printf("%d\n",Ans);
}
I void clear(){Me(s.h,0);s.head=0;S1.clear();S2.clear();Me(son,0);idea=0;}
I void Solve(){
re int i;clear();;scanf("%d%d",&n,&m);for(i=1;i<n;i++)read(x),read(y),s.add(x,y),s.add(y,x);
dfs1(1,0);dfs2(1,1);for(i=1;i<=m;i++)read(op),read(x),read(y),op^2?change(x,y,i):GetAns(x,y,i);
}
int main(){
// freopen("edge.in","r",stdin);freopen("edge.out","w",stdout);
scanf("%d",&T);while(T--) Solve();
}
D1T2
首先这个奇偶做差的形式不难想到行列式。
手玩一下\(k=2\)的爆搜发现就是枚举一个排列,然后算出逆序对个数和这个排列是否可行。
那么就是求这个矩阵的行列式的值。
这个做法也可以扩展到\(n1=n2\),因为所有方案独立所以乘起来就好了。
考虑点数不一样怎么做。
发现如果直接算连通性对奇偶性影响不变。
所以可以矩阵乘法计算出两边点的连通性。然后行列式即可。
时间复杂度\(O(n^4)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 200
#define W (1<<18)
#define mod 998244353
#define eps (1e-5)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
using namespace std;
int T,n[N+5],m[N+5],k,x,y,flag;ll A[N+5][N+5],now,Ans,B[N+5][N+5],C[N+5][N+5];
I ll mpow(ll x,int y=mod-2){ll ans=1;while(y) (y&1)&&(ans=ans*x%mod),y>>=1,x=x*x%mod;return ans;}
I void swap(ll &x,ll &y){x^=y^=x^=y;}
I ll calc(int n){
re int i,j,h;ll Ans=1;for(i=1;i<=n;i++){
for(j=i+1;j<=n;j++){
while(A[j][i]){
now=mod-A[i][i]/A[j][i];for(h=i;h<=n;h++)A[i][h]=(A[i][h]+A[j][h]*now)%mod,swap(A[i][h],A[j][h]);Ans*=-1;
}
}
}for(i=1;i<=n;i++) Ans=Ans*A[i][i]%mod;return (Ans+mod)%mod;
}
I void cheng(int n,int m,int k){
re int i,j,h;for(i=1;i<=n;i++){
for(j=1;j<=k;j++){
C[i][j]=0;for(h=1;h<=m;h++) C[i][j]=(C[i][j]+A[i][h]*B[h][j])%mod;
}
}Me(A,0);for(i=1;i<=n;i++) for(j=1;j<=k;j++) A[i][j]=C[i][j];
}
I void Solve(){
re int i,j;scanf("%d",&k);for(i=1;i<=k;i++) scanf("%d",&n[i]);for(i=1;i<k;i++) scanf("%d",&m[i]);Me(A,0);
while(m[1]--) scanf("%d%d",&x,&y),A[x][y]++;
for(i=2;i<k;i++){
Me(B,0);while(m[i]--) scanf("%d%d",&x,&y),B[x][y]++;cheng(n[i-1],n[i],n[i+1]);
}
printf("%lld\n",calc(n[1]));
}
int main(){
// freopen("xpath.in","r",stdin);freopen("xpath.out","w",stdout);
scanf("%d",&T);while(T--) Solve();
}
D1T3
不会,先咕着
D2T1
史上最简单D2T1
首先我们考虑这个\(k\)这么小有什么用。
所以绝大部分都是相同的。
又因为数据随机,所以考虑乱搞。
我们将256位每16位一段,因为\(k\leq 15\)所以必定要至少在一个块中相同。
因为数据随机,所以每个块中我们期望查询\(7\)个,总共查询\(7\times 16\)个。
每次查询需要\(8\)的常数,所以总时间复杂度\(O(256n+896m)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 400000
#define W 256
#define M 16
#define mod 1000000007
#define eps (1e-5)
#define ull unsigned ll
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
I void read(int &x){char s=Gc();x=0;while(s<'0'||s>'9') s=Gc();while(s>='0'&&s<='9') x=x*10+s-48,s=Gc();}
using namespace std;
int n,m,k,lastans,now,fr[M+5],en[M+5];ull a1,a2;vector<int> Id[M+5][W*W+5];bitset<W+5> A[N+5],C;char B[W+5];
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++)
A[i][j] = (myRand(a1, a2) & (1ull << 32)) ? 1 : 0;
}
int main(){
// freopen("qi.in","r",stdin);freopen("qi.out","w",stdout);
re int i,j,h;scanf("%d%d%llu%llu",&n,&m,&a1,&a2);gen(n,a1,a2);for(i=1;i<=M;i++) fr[i]=i*16-16,en[i]=i*16-1;
for(j=1;j<=M;j++){
for(i=1;i<=n;i++){
for(now=0,h=fr[j];h<=en[j];h++) now=(now<<1)+A[i][h];Id[j][now].push_back(i);
}
}while(m--){
scanf("%s%d",B,&k);for(i=0;i<W/4;i++){
B[i]=(B[i]>'9')?10+B[i]-'A':B[i]-'0';
for(j=0;j<=3;j++) C[(i+1)*4-j-1]=((B[i]>>j)&1)^lastans;
}lastans=0;
for(i=1;i<=M;i++) {
for(now=0,j=fr[i];j<=en[i];j++) now=(now<<1)+C[j];
for(j=0;j<Id[i][now].size();j++) if((C^A[Id[i][now][j]]).count()<=k){lastans=1;break;};if(lastans) break;
}printf("%d\n",lastans);
}
}
D2T2
因为通分过程中的\(x+\frac{y}{z}=\frac{xz+y}{z}\)肯定是互质的所以一定是两个分开计算然后取模即可。
设向量\((x,y)\)表示答案\(\frac{x}{y}\),那么加入一个数\(k\)对应\(k+\frac{y}{x}=\frac{xk+y}{x}\)即乘上矩阵\(\begin{bmatrix}k&&1\\&&\\1&&0\end{bmatrix}\)
因为这道题是在右边加数,所以我们的矩阵乘法是自右向左计算的。
考虑W操作,就是对最后一个数加一,那么在最右边乘上矩阵\(\begin{bmatrix}1&&1\\&&\\0&&1\\ \end{bmatrix}\)即可。
然后考虑E操作,发现是一个分类讨论但是本质相同,所以先减一再加上两个一即可,即乘上\(\begin{bmatrix}1&&1\\&&\\1&&0\end{bmatrix}^2\begin{bmatrix}1&&-1\\&&\\0&&1\end{bmatrix}=\begin{bmatrix}2&&-1\\&&\\1&&0\end{bmatrix}\)
因为有区间翻转操作所以考虑平衡树。对于每个节点维护操作反过来和不反过来,操作逆序和不逆序四个值即可,注意到标记的先后对于答案没有影响所以随便处理即可。
时间复杂度大常数\(O(2^3nlogn)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 200000
#define mod 998244353
#define eps (1e-5)
#define ull unsigned ll
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
using namespace std;
int n,m,k,x,y;char S[N+5],C[N+5];
struct matrix{
ll A[2][2];
matrix operator *(const matrix &B)const{
re int i,j,h;matrix C;Me(C.A,0);for(i=0;i<2;i++){
for(j=0;j<2;j++){
for(h=0;h<2;h++)C.A[i][j]=(C.A[i][j]+A[i][h]*B.A[h][j])%mod;
}
}return C;
}
}Cl,B1,B2,B3,now;
struct FHQ_Treap{
matrix S1[N+5],S2[N+5],S3[N+5],S4[N+5],V1[N+5],V2[N+5];int FF[N+5],FR[N+5],key[N+5],siz[N+5],cnt,l[N+5],r[N+5],root,root1,root2,root3;
I void build(){S1[0]=S2[0]=S3[0]=S4[0]=Cl;}
I void Up(int now){siz[now]=siz[l[now]]+siz[r[now]]+1;S1[now]=S1[r[now]]*V1[now]*S1[l[now]];S2[now]=S2[r[now]]*V2[now]*S2[l[now]];S3[now]=S3[l[now]]*V1[now]*S3[r[now]];S4[now]=S4[l[now]]*V2[now]*S4[r[now]];}
I int newnode(int x){key[++cnt]=rand()*rand();x?(V1[cnt]=B1,V2[cnt]=B2):(V1[cnt]=B2,V2[cnt]=B1);Up(cnt);return cnt;}
I void pushF(int now){now&&(swap(V1[now],V2[now]),swap(S1[now],S2[now]),swap(S3[now],S4[now]),FF[now]^=1);}I void pushR(int now){now&&(swap(l[now],r[now]),swap(S1[now],S3[now]),swap(S2[now],S4[now]),FR[now]^=1);}
I void push(int now){FF[now]&&(pushF(l[now]),pushF(r[now]),FF[now]=0);FR[now]&&(pushR(l[now]),pushR(r[now]),FR[now]=0);}
I void split(int x,int now,int &a,int &b){
if(!now) return (void)(a=b=0);push(now);siz[l[now]]+1<=x?(a=now,split(x-siz[l[now]]-1,r[a],r[a],b)):(b=now,split(x,l[b],a,l[b]));Up(now);
}
I int merge(int x,int y){
if(!x||!y) return x|y;push(x);push(y);return key[x]<key[y]?(r[x]=merge(r[x],y),Up(x),x):(l[y]=merge(x,l[y]),Up(y),y);
}
I void insert(int x){root=merge(root,newnode(x));}
I void GetF(int x,int y){split(x-1,root,root1,root2);split(y-x+1,root2,root2,root3);pushF(root2);root=merge(root1,merge(root2,root3));}
I void GetR(int x,int y){split(x-1,root,root1,root2);split(y-x+1,root2,root2,root3);pushR(root2);root=merge(root1,merge(root2,root3));}
I void GetAns(){now=S1[root]*B3;printf("%lld %lld\n",now.A[0][1],now.A[0][0]);}
}T;
int main(){
//freopen("code.in","r",stdin);freopen("code.out","w",stdout);
re int i;Cl.A[1][1]=Cl.A[0][0]=1;B1.A[0][0]=B1.A[0][1]=B1.A[1][1]=1;B2.A[0][0]=2,B2.A[1][0]=1,B2.A[0][1]=mod-1;B3.A[0][0]=B3.A[1][0]=B3.A[0][1]=1;
scanf("%d%d",&n,&m);k=n+m;srand((unsigned int)time(0));T.build();scanf("%s",S+1);for(i=1;i<=n;i++) T.insert(S[i]=='W');T.GetAns();while(m--){
scanf("%s",C+1);if(C[1]=='A') scanf("%s",C),T.insert(C[0]=='W');
else scanf("%d%d",&x,&y),(C[1]=='F')?T.GetF(x,y):T.GetR(x,y);T.GetAns();
}
}
D2T3
不会咕咕咕