Codeforces Round #539 Div1 題解
聽說這場很適合上分QwQ
然而太晚了QaQ
A. Sasha and a Bit of Relax
翻譯
有一個長度為\(n\)的數組,問有多少個長度為偶數的連續區間,使得其前一半的異或和等於后一半的異或和。
題解
顯然就是求長度為偶數且異或和為\(0\)的區間個數
求異或和為\(0\)的區間個數很簡單,對於整個區間求異或前綴和看看有多少個相等就好了。
求長度為偶數的也很簡單,把每個位置的異或前綴和按照位置的奇偶性分開求個數每次計算一下后面和當前位置的異或前綴和相等的個數就好了。
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 300300
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,a[MAX];
int s[2][1<<20];
ll ans;
int main()
{
n=read();
for(int i=1;i<=n;++i)a[i]=read()^a[i-1],s[i&1][a[i]]+=1;
for(int i=1;i<=n;++i)ans+=s[i&1^1][a[i-1]],s[i&1][a[i]]-=1;
cout<<ans<<endl;
return 0;
}
B. Sasha and One More Name
翻譯
給定一個回文串,你可以把它分成若干段,然后隨意的交換他們的順序,使得最終得到一個和原串不同的回文串。求分段的最小次數。
題解
首先不合法一定是除了回文中心外,所有字符都相同的串。
只有有解的話,兩次操作即可完成。即找到一個包含兩個以上不同字符的非回文串前綴,然后把前綴后綴的這個串交換。
現在考慮什么情況下只需要一次操作,那么枚舉一個斷點,交換一下,檢查是否是回文串即可。這個因為串長很小直接暴力就行了。(我哈希自然溢出被卡了嚶嚶嚶)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
#define MAX 5050
int n;char s[MAX],ch[MAX];
void check0()
{
for(int i=1;i<=n/2;++i)if(s[i]!=s[1])return;
puts("Impossible");exit(0);
}
void check1()
{
for(int i=1;i<n;++i)
{
int len=0;bool fl=true;
for(int j=i+1;j<=n;++j)ch[++len]=s[j];
for(int j=1;j<=i;++j)ch[++len]=s[j];
for(int j=1;j<=n;++j)if(ch[j]!=s[j]){fl=false;break;}
if(fl)continue;
for(int j=1;j<=n/2;++j)if(ch[j]!=ch[n-j+1]){fl=true;break;}
if(!fl)puts("1"),exit(0);
}
}
int main()
{
scanf("%s",s+1);n=strlen(s+1);
check0();check1();puts("2");
return 0;
}
C. Sasha and a Patient Friend
翻譯
有一只鴿子有一顆強大的內心,然而內心中的鴿值卻並不是無限的。
一開始它的內心有\(V\)單位鴿值,然后它的內心中有一個水龍頭,會每個單位時間向外流出\(s\)單位鴿值(\(s\)可以為負數,會改變,初始時\(s=0\)),當這只鴿子沒有鴿值的時候它就會徹底咕咕咕。
現在你是\(zsy\),你進行了\(Q\)次操作,操作有以下幾種:
- 從第\(t\)時刻開始,把水龍頭的速度修改為\(s\)
- 刪除從\(t\)時刻開始的修改操作
- 以\(l\)時刻作為初始時刻,打開這只鴿子內心的水龍頭,在\(r\)時刻關上。回答這只鴿子是否會咕咕咕,如果會 回答在什么時刻咕咕咕(注意這個時間可能是小數),如果不會輸出\(-1\)。詢問之間獨立。
保證在操作進行的過程中,任意相同時刻不會同時出現兩次修改操作。
題解
不要在意辣雞翻譯。
顯然要把所有的修改按照時間排個序之類的操作,所以掏出了平衡樹。
對於每個節點,維護其這個修改維持的時間以及流出的估值的數量,維護區間內每個修改結束時流出的最大值,這樣子單次詢問只需要在平衡樹上二分就好了。
看代碼吧。。。(代碼有點亂)
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 100100
#define lson (t[x].ch[0])
#define rson (t[x].ch[1])
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Data{int k,t;ll s,mxs;};
Data operator+(Data a,Data b)
{
Data c;c.k=b.k;
c.s=a.s+b.s;c.t=a.t+b.t;
c.mxs=max(a.mxs,b.mxs+a.s);
return c;
}
struct Node
{
int ff,ch[2];
int k,t,len;
Data a;
Data Pre(){return (Data){k,len,1ll*k*len,max(1ll*k*len,0ll)};}
}t[MAX];
int rt,tot;
void pushup(int x){t[x].a=t[lson].a+t[x].Pre()+t[rson].a;}
void rotate(int x)
{
int y=t[x].ff,z=t[y].ff;
int k=t[y].ch[1]==x;
if(z)t[z].ch[t[z].ch[1]==y]=x;t[x].ff=z;
t[y].ch[k]=t[x].ch[k^1];if(t[x].ch[k^1])t[t[x].ch[k^1]].ff=y;
t[x].ch[k^1]=y;t[y].ff=x;
pushup(y);pushup(x);
}
void Splay(int x,int goal)
{
while(t[x].ff!=goal)
{
int y=t[x].ff,z=t[y].ff;
if(z!=goal)
(t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
rotate(x);
}
if(!goal)rt=x;
}
void Insert(int a,int s)
{
int x=rt,f=0;
while(233)
{
if(t[x].t>a)f=x,x=lson;
else f=x,x=rson;
if(!x)break;
}
int k=t[f].t<a;x=++tot;
if(f)t[f].ch[k]=x;t[x].ff=f;
t[x].t=a;t[x].k=s;Splay(x,0);
}
int Next(int a,int p)
{
int x=rt,u=0;bool fl=false;
while(233)
{
if(t[x].t==a){fl=true;break;}
int k=t[x].t<a;
if(p^k)u=x;
if(!t[x].ch[k])break;
x=t[x].ch[k];
}
if(!fl)return u;
if(!t[x].ch[p])return u;
else x=t[x].ch[p];
while(t[x].ch[p^1])x=t[x].ch[p^1];
return x;
}
void Adding(int a,int s)
{
Insert(a,s);
int nw=tot,lt=Next(a,0),nt=Next(a,1);
Splay(lt,0);t[lt].len=a-t[lt].t;pushup(lt);
Splay(nw,0);t[nw].len=t[nt].t-a;pushup(nw);
}
void Delete(int a)
{
int uL=Next(a,0),uR=Next(a,1);
Splay(uL,0);Splay(uR,uL);
t[uR].ch[0]=0;
pushup(uR);
t[uL].len=t[uR].t-t[uL].t;
pushup(uL);
}
double Query(int l,int r,ll V)
{
if(!V)return l;
int uL=Next(l,0),uR=Next(r,1),QwQ;
Splay(uL,0);Splay(uR,uL);
int nw=t[uR].ch[0];QwQ=nw;
if(t[nw].a.mxs<V)return -1;
while(t[QwQ].ch[0])QwQ=t[QwQ].ch[0];
int u=nw;Data now=(Data){0,0,0,0};
while(233)
{
Data pre=now+t[t[u].ch[0]].a+t[u].Pre();
if(pre.mxs<V)now=pre,u=t[u].ch[1];
else
{
Data qwq=now+t[t[u].ch[0]].a;
if(qwq.mxs<V)return 1.0*(V-qwq.s)/t[u].k+t[u].t;
u=t[u].ch[0];
}
if(!u)return -1;
}
}
int main()
{
Insert(0,0);Insert(2e9+1,0);
int Q=read();
while(Q--)
{
int opt=read();
if(opt==1)
{
int a=read(),s=read();
Adding(a,-s);
}
else if(opt==2)Delete(read());
else
{
int l=read(),r=read(),V=read();
double s=Query(l,r,V);
if(s>r)puts("-1");
else printf("%.10lf\n",s);
}
}
return 0;
}
D. Sasha and Interesting Fact from Graph Theory
翻譯
有一個\(n\)個節點的樹,邊權是\([1,m]\)中的任意整數,現在問你有多少棵樹滿足\(a,b\)之間的路徑權值和為\(m\)。
題解
首先枚舉\(a,b\)之間的點數\(i\),這里選出\(i\)個點然后連成一條鏈,方案數是\({n-2\choose i}*i!\)。
然后把權值和\(m\)分配到這\(i+1\)條邊上,方案數是\({m-1\choose i}\)。
然后剩下的點任意連上來形成樹,方案數是\(n^{n-i-3}*(i+2)\)。
然后剩下的邊的權值任意,方案數是\(m^{n-2-i}\)。
加起來就完了。
#include<iostream>
#include<cstdio>
using namespace std;
#define MOD 1000000007
#define MAX 2000100
int n,m,a,b,ans,jc[MAX],jv[MAX];
int fpow(int a,int b)
{
int s=1;if(b<0)b+=MOD-1;
while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
return s;
}
int C(int n,int m){if(n<m||n<0||m<0)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
int main()
{
scanf("%d%d%d%d",&n,&m,&a,&b);
jc[0]=jv[0]=jv[1]=1;
for(int i=2;i<=n+m;++i)jv[i]=1ll*jv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n+m;++i)jc[i]=1ll*jc[i-1]*i%MOD;
for(int i=1;i<=n+m;++i)jv[i]=1ll*jv[i-1]*jv[i]%MOD;
for(int i=0;i<=n-2;++i)
ans=(ans+1ll*C(n-2,i)*jc[i]%MOD*C(m-1,i)%MOD*fpow(n,n-i-3)%MOD*(i+2)%MOD*fpow(m,n-2-i))%MOD;
printf("%d\n",ans);
return 0;
}
E. Sasha and a Very Easy Test
翻譯
維護一個數列,支持以下操作
- 區間乘法
- 單點除法,保證能夠整除
- 模意義下的區間和
模數提前給定,不保證是質數。
題解
因為模數不保證是質數,所以現在的問題在於怎么單點除法。
其實還是很呆。
把模數質因數分解,維護兩棵線段樹,一棵記錄原數,一棵記錄去除所有模數的質因子后的值。
每次區間乘法兩棵同時改。
現在的問題在於區間除法,再開一個\(BIT\)記錄區間每個模數的質因子被乘的次數,這樣子就可以計算除完后的每個質因子的個數,然后兩棵線段樹分別修改即可。
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 100100
#define lson (now<<1)
#define rson (now<<1|1)
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int fpow(int a,int b,int MOD){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
void exgcd(int a,int b,int &x,int &y){if(!b){x=1;y=0;return;}exgcd(b,a%b,y,x);y-=a/b*x;}
int Inv(int a,int MOD){int x,y;exgcd(a,MOD,x,y);x=(x%MOD+MOD)%MOD;return x;}
int n,MOD,Q,a[MAX],fac[10],tot;
int p[10][MAX];
struct SegMentTree
{
int t[MAX<<2],tag[MAX<<2];
void Build(int now,int l,int r)
{
tag[now]=1;
if(l==r){t[now]=a[l]%MOD;return;}
int mid=(l+r)>>1;
Build(lson,l,mid);Build(rson,mid+1,r);
t[now]=(t[lson]+t[rson])%MOD;
}
void puttag(int now,int w){t[now]=1ll*t[now]*w%MOD;tag[now]=1ll*tag[now]*w%MOD;}
void pushdown(int now)
{
if(tag[now]==1)return;
puttag(lson,tag[now]);puttag(rson,tag[now]);
tag[now]=1;
}
void Modify(int now,int l,int r,int p,int w)
{
if(l==r){t[now]=w;return;}
int mid=(l+r)>>1;pushdown(now);
if(p<=mid)Modify(lson,l,mid,p,w);
else Modify(rson,mid+1,r,p,w);
t[now]=(t[lson]+t[rson])%MOD;
}
void Modify_mul(int now,int l,int r,int L,int R,int w)
{
if(L<=l&&r<=R){puttag(now,w);return;}
int mid=(l+r)>>1;pushdown(now);
if(L<=mid)Modify_mul(lson,l,mid,L,R,w);
if(R>mid)Modify_mul(rson,mid+1,r,L,R,w);
t[now]=(t[lson]+t[rson])%MOD;
}
int Query(int now,int l,int r,int L,int R)
{
if(L<=l&&r<=R)return t[now];
int mid=(l+r)>>1,ret=0;pushdown(now);
if(L<=mid)ret=(ret+Query(lson,l,mid,L,R))%MOD;
if(R>mid)ret=(ret+Query(rson,mid+1,r,L,R))%MOD;
return ret;
}
}T1,T2;
int lb(int x){return x&(-x);}
struct BIT
{
int c[MAX];
void add(int x,int w){while(x<=n)c[x]+=w,x+=lb(x);}
int getsum(int x){int s=0;while(x)s+=c[x],x-=lb(x);return s;}
void Modify(int l,int r,int w){add(l,w);add(r+1,-w);}
}T[10];
int Div[10];
int Fact(int x)
{
for(int i=1;i<=tot;++i)Div[i]=0;
for(int i=1;i<=tot;++i)
while(x%fac[i]==0)Div[i]+=1,x/=fac[i];
return x;
}
int main()
{
n=read();MOD=read();
for(int i=1;i<=n;++i)a[i]=read();
int P=MOD;
for(int i=2;i*i<=MOD;++i)
if(P%i==0){fac[++tot]=i;while(P%i==0)P/=i;}
if(P>1)fac[++tot]=P;
T1.Build(1,1,n);
for(int i=1;i<=n;++i)
for(int j=1;j<=tot;++j)
while(a[i]%fac[j]==0)++p[j][i],a[i]/=fac[j];
T2.Build(1,1,n);
Q=read();
while(Q--)
{
int opt=read();
if(opt==1)
{
int l=read(),r=read(),x=read();
T2.Modify_mul(1,1,n,l,r,Fact(x)%MOD);
for(int i=1;i<=tot;++i)T[i].Modify(l,r,Div[i]);
T1.Modify_mul(1,1,n,l,r,x%MOD);
}
else if(opt==2)
{
int l=read(),x=read(),c=Fact(x);
for(int i=1;i<=tot;++i)p[i][l]-=Div[i];
for(int i=1;i<=tot;++i)Div[i]=p[i][l]+T[i].getsum(l);
c=1ll*T2.Query(1,1,n,l,l)*Inv(c,MOD)%MOD;
T2.Modify(1,1,n,l,c);
for(int i=1;i<=tot;++i)c=1ll*c*fpow(fac[i],Div[i],MOD)%MOD;
T1.Modify(1,1,n,l,c);
}
else
{
int l=read(),r=read();
printf("%d\n",T1.Query(1,1,n,l,r));
}
}
return 0;
}
F. Sasha and Algorithm of Silence's Sounds
翻譯
有一個\(n*m\)的網格圖,每個格子里填着\([1,n*m]\)中的某個數,每個數恰好出現一次。
現在問有多少個\([l,r]\),滿足這些數字所在的格子恰好構成了一棵樹(即一個聯通塊,且無環)
題解
首先可以對於每個\(r\),用\(LCT\)維護出其最小的\(L_r\),滿足\([L_r,r]\)構成森林。
那么現在的問題就是找到\(l\in[L_r,r]\),使得\([l,r]\)構成了一個聯通塊。
設\(s[i]\)表示\([i,r]\)區間內點構成的聯通塊數量,顯然在構成森林的情況下,聯通塊個數等於點數減去邊數,那么我們可以用線段樹維護區間減法來維護點數減去邊數,這樣子只需要統計\(1\)的個數,因為\(1\)必定是最小值,所以計算最小值以及最小值個數就好了。
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define ls (t[x].ch[0])
#define rs (t[x].ch[1])
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Node{int ff,ch[2],rev;}t[200200];
bool isroot(int x){return t[t[x].ff].ch[0]!=x&&t[t[x].ff].ch[1]!=x;}
void rotate(int x)
{
int y=t[x].ff,z=t[y].ff;
int k=t[y].ch[1]==x;
if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;t[x].ff=z;
t[y].ch[k]=t[x].ch[k^1];t[t[x].ch[k^1]].ff=y;
t[x].ch[k^1]=y;t[y].ff=x;
}
void pushdown(int x){if(t[x].rev)t[ls].rev^=1,t[rs].rev^=1,swap(ls,rs),t[x].rev^=1;}
int S[200200],top;
void Splay(int x)
{
S[top=1]=x;
for(int i=x;!isroot(i);i=t[i].ff)S[++top]=t[i].ff;
while(top)pushdown(S[top--]);
while(!isroot(x))
{
int y=t[x].ff,z=t[y].ff;
if(!isroot(y))
(t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
rotate(x);
}
}
void access(int x){for(int y=0;x;y=x,x=t[x].ff)Splay(x),rs=y;}
void makeroot(int x){access(x);Splay(x);t[x].rev^=1;}
void split(int x,int y){makeroot(x);access(y);Splay(y);}
void link(int x,int y){makeroot(x);t[x].ff=y;}
void cut(int x,int y){split(x,y);t[y].ch[0]=t[x].ff=0;}
int findroot(int x){access(x);Splay(x);while(ls)x=ls;return x;}
int n,m;ll ans;
int a[1010][1010];
int X[200200],Y[200200];
int d[4][2]={1,0,0,1,-1,0,0,-1};
struct SegMent
{
#define lson (now<<1)
#define rson (now<<1|1)
struct Node{int v,s;}t[200200<<2];int tag[200200<<2];
Node Merge(Node a,Node b)
{
Node c;
c.v=min(a.v,b.v);c.s=0;
if(c.v==a.v)c.s+=a.s;
if(c.v==b.v)c.s+=b.s;
return c;
}
void pushup(int now){t[now]=Merge(t[lson],t[rson]);}
void Build(int now,int l,int r)
{
if(l==r){t[now]=(Node){1,1};return;}
int mid=(l+r)>>1;
Build(lson,l,mid);Build(rson,mid+1,r);
pushup(now);
}
void puttag(int now,int w){t[now].v+=w;tag[now]+=w;}
void pushdown(int now){puttag(lson,tag[now]);puttag(rson,tag[now]);tag[now]=0;}
void Modify(int now,int l,int r,int L,int R,int w)
{
if(L<=l&&r<=R){puttag(now,w);return;}
int mid=(l+r)>>1;pushdown(now);
if(L<=mid)Modify(lson,l,mid,L,R,w);
if(R>mid)Modify(rson,mid+1,r,L,R,w);
pushup(now);
}
Node Query(int now,int l,int r,int L,int R)
{
if(L==l&&r==R)return t[now];
int mid=(l+r)>>1;pushdown(now);
if(R<=mid)return Query(lson,l,mid,L,R);
if(L>mid)return Query(rson,mid+1,r,L,R);
return Merge(Query(lson,l,mid,L,mid),Query(rson,mid+1,r,mid+1,R));
}
int Query(int l,int r){Node a=Query(1,1,n*m,l,r);return a.v==1?a.s:0;}
}T;
bool check(int l,int r,int p)
{
for(int i=0;i<4;++i)
{
int x=X[p]+d[i][0],y=Y[p]+d[i][1];
if(x<0||y<0||x>n||y>m)continue;
if(!(l<=a[x][y]&&a[x][y]<=r))continue;
for(int j=i+1;j<4;++j)
{
int xx=X[p]+d[j][0],yy=Y[p]+d[j][1];
if(xx<0||yy<0||xx>n||yy>m)continue;
if(!(l<=a[xx][yy]&&a[xx][yy]<=r))continue;
if(findroot(a[x][y])==findroot(a[xx][yy]))return false;
}
}
return true;
}
void Work(int l,int r,int p,int opt)
{
for(int i=0;i<4;++i)
{
int x=X[p]+d[i][0],y=Y[p]+d[i][1];
if(x<0||y<0||x>n||y>m)continue;
if(!(l<=a[x][y]&&a[x][y]<=r))continue;
if(opt==-1)cut(p,a[x][y]);
else link(p,a[x][y]),T.Modify(1,1,n*m,1,a[x][y],-1);
}
}
int main()
{
n=read();m=read();T.Build(1,1,n*m);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)a[i][j]=read();
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)X[a[i][j]]=i,Y[a[i][j]]=j;
for(int i=1,p=1;i<=n*m;++i)
{
while(!check(p,i-1,i))Work(p+1,i-1,p,-1),++p;
Work(p,i-1,i,1);
ans+=T.Query(p,i);
T.Modify(1,1,n*m,p,i,1);
}
printf("%I64d\n",ans);
return 0;
}