CSP 后多校一
A. 树上的数
考场上没做出来,太不应该.
其实只要算一算复杂度就应该知道要写 \(O(n)\) 的算法,而且自己想一会儿没有想到 \(O(n)\) 也确实不应该是这个阶段的水平.
很显然地发现如果一个点扫过了那么子树就都不用扫了,所以直接做.
A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
#define ll int
#define lf double
#define ull unsigned ll
#define mp make_pair
#define lb lower_bound
#define ub upper_bound
#define lbt(x) ((x)&(-(x)))
#define Fill(x,y) memset(x,y,sizeof(x))
#define Copy(x,y) memcpy(x,y,sizeof(x))
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
auto read=[]()->ll{
ll w=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
return cit?w:(-w);
};
} using namespace BSS;
#define ls (x<<1)
#define rs (x<<1|1)
const ll N=5e6+21,R=19760817;
long long int A,B,X,Y;
ll m,n,ts,cnt,ans,fans;
ll fa[N],qi[N],dfn[N],head[N],siz[N],vis[N];
struct I { ll u,v,nxt; } e[N<<1];
struct II { ll w,lzy; } tr[N<<2];
auto add=[](ll u,ll v)->void{
e[++ts].u=u,e[ts].v=v,e[ts].nxt=head[u];
head[u]=ts;
};
void dfs(ll u){
if(vis[u]) return ; ans--,vis[u]=1;
for(ll i=head[u];i;i=e[i].nxt) dfs(e[i].v);
}
signed main(){
File(tree);
ll u,v,w;
n=read(),m=read(),A=read(),B=read(),fa[2]=1,qi[1]=read(),X=read(),Y=read(),ans=n;
for(ll i=3;i<=n;i++) fa[i]=((1ll*fa[i-1]*A+B)^R)%(i-1)+1;
for(ll i=2;i<=n;i++) add(fa[i],i);
for(ll i=2;i<=m;i++) qi[i]=(((1ll*qi[i-1]*X+Y)^R)^(i<<1ll))%(n-1)+2;
for(ll i=1;i<=m;i++){
u=qi[i],dfs(u),fans^=ans;
}
printf("%d\n",fans),exit(0);
}
B. 时代的眼泪
想到了换根 \(dp\),但是没写出来,不会转移信息直接死了.
其实换根 \(dp\) 难算的就是子树内的信息,但是其实差分可以很好地解决这一问题.
比如算 \(x\) 之后的值减去算 \(x\) 之前的值就可以得到 \(x\) 的值等等.
这里其实应该学会这个思想,难算的能不能转化是一个很好的思考方向.
B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
#define ll int
#define lf double
#define ull unsigned ll
#define mp make_pair
#define lb lower_bound
#define ub upper_bound
#define lbt(x) ((x)&(-(x)))
#define Fill(x,y) memset(x,y,sizeof(x))
#define Copy(x,y) memcpy(x,y,sizeof(x))
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
auto read=[]()->ll{
ll w=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
return cit?w:(-w);
};
} using namespace BSS;
const ll N=1e6+21;
ll m,n,ts,cnt,ops;
ll cf[N],val[N],rk[N],dfn[N],lsh[N],head[N];
long long int ans[N];
struct I { ll x,y,res; } q[N];
struct II { ll u,v,nxt; } e[N<<1];
auto add=[](ll u,ll v)->void{
e[++ts].u=u,e[ts].v=v,e[ts].nxt=head[u];
head[u]=ts;
};
struct Bit_Array{
ll res; ll c[N<<1];
inline void update(ll x,ll w){
for(;x;x&=x-1) c[x]+=w;
}
inline ll query(ll x){
for(res=0;x<=n;x+=lbt(x)) res+=c[x]; return res;
}
}A;
struct FenWick{
ll res; ll c[N<<1];
inline void update(ll x,ll w){
for(x++;x<=n+1;x+=lbt(x)) c[x]+=w;
}
inline ll query(ll x){
for(x++,res=0;x;x&=x-1) res+=c[x]; return res;
}
inline void del(ll x){
for(x++;x<=n+1;x+=lbt(x)) c[x]=0;
for(x++;x;x&=x-1) c[x]=0;
}
}B;
void dfs1(ll u,ll dad){
ans[1]+=A.query(rk[u]+1),A.update(rk[u],1);
cf[u]=B.query(rk[u]-1)+B.query(rk[dad]-1),B.update(rk[u],1);
for(ll i=head[u];i;i=e[i].nxt){
if(e[i].v==dad) continue;
dfs1(e[i].v,u);
}
cf[u]=cf[u]-B.query(rk[u]-1)-B.query(rk[dad]-1),A.update(rk[u],-1);
}
void dfs2(ll u,ll dad){
if(dad) ans[u]=ans[dad]+1ll*B.query(rk[u]-1)+1ll*cf[u];
for(ll i=head[u];i;i=e[i].nxt){
if(e[i].v==dad) continue;
dfs2(e[i].v,u);
}
}
signed main(){
File(tears);
n=read(),ops=read(); ll u,v,flag1=1,flag2=1;
for(ll i=1;i<=n;i++) val[i]=read(),lsh[i]=val[i];
sort(lsh+1,lsh+1+n),cnt=unique(lsh+1,lsh+1+n)-lsh-1;
for(ll i=1;i<=n;i++) rk[i]=lb(lsh+1,lsh+1+cnt,val[i])-lsh;
for(ll i=2;i<=n;i++) u=read(),v=read(),add(u,v),add(v,u);
dfs1(1,0);
for(ll i=1;i<=n;i++) B.del(rk[i]);
for(ll i=1;i<=n;i++) B.update(rk[i],1);
dfs2(1,0);
while(ops--) printf("%lld\n",ans[read()]);
exit(0);
}
C. 传统艺能
看到有一点不太好做就跳过去了,连 \(dp\) 都没想,因为觉得 \(T4\) 更好写.
这个题其实也不是很难,之前也见过类似的套路题,从 \(Yubai\) 那里学来了通式通法,
即:\(f_i=\sum\limits_{c}f_c+1\).
由于是递推过去的,很显然可以使用矩阵优化.
另外,矩阵不满足交换律,但是在以下两种情况仍然可以交换(还是 \(Yubai\) 教我的):
-
只有主对角线有值.
-
两个矩阵乘起来之后是单位矩阵.
所以矩阵不满足交换律的,我们可以先算右边,之后把该在左边乘的移到左边去.
之前多次感觉自己矩阵学的不好,又多次想办法好好补一补学一学,发现自己的措施仍然不尽人意.
C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
#define ll long long
#define lf double
#define ull unsigned ll
#define mp make_pair
#define lb lower_bound
#define ub upper_bound
#define lbt(x) ((x)&(-(x)))
#define Fill(x,y) memset(x,y,sizeof(x))
#define Copy(x,y) memcpy(x,y,sizeof(x))
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
auto read=[]()->ll{
ll w=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
return cit?w:(-w);
};
} using namespace BSS;
#define ls (x<<1)
#define rs (x<<1|1)
const ll N=1e5+21,mod=998244353;
ll m,n,ops;
ll val[N];
struct Mat{
ll w[5][5];
inline void clr(){
for(ll i=1;i<=4;i++){
for(ll j=1;j<=4;j++) w[i][j]=0;
}
}
inline void Put(){
for(ll i=1;i<=4;i++){
for(ll j=1;j<=4;j++) cout<<w[i][j]<<' ';
puts("");
}
puts("");
}
}o,mc[4],tr[N<<2];
inline void mul(Mat &c,Mat a,Mat b){
for(ll i=1;i<=4;i++){
for(ll j=1;j<=4;j++){
c.w[i][j]=0;
for(ll k=1;k<=4;k++)
c.w[i][j]=(c.w[i][j]+a.w[i][k]*b.w[k][j]%mod)%mod;
}
}
}
void build(ll x,ll l,ll r){
if(l==r) return tr[x]=mc[val[l]],void();
ll mid=(l+r)>>1; build(ls,l,mid),build(rs,mid+1,r);
mul(tr[x],tr[ls],tr[rs]);
}
void update(ll x,ll l,ll r,ll pos){
if(l==r) return tr[x]=mc[val[pos]],void();
ll mid=(l+r)>>1;
pos<=mid ? update(ls,l,mid,pos) : update(rs,mid+1,r,pos);
mul(tr[x],tr[ls],tr[rs]);
}
Mat query(ll x,ll l,ll r,ll ql,ll qr){
if(l>=ql and r<=qr) return tr[x];
ll mid=(l+r)>>1,flag=0; Mat res;
if(ql<=mid) res=query(ls,l,mid,ql,qr),flag=1;
if(qr>mid){
if(flag) mul(res,res,query(rs,mid+1,r,ql,qr));
else res=query(rs,mid+1,r,ql,qr);
}
return res;
}
signed main(){
File(string);
for(ll i=1;i<=3;i++){
for(ll j=1;j<=4;j++) mc[i].w[j][i]=1;
for(ll j=1;j<=4;j++) mc[i].w[j][j]=1;
}
o.w[1][4]=1;
char ch[N]; ll opt,x,y,l,r;
n=read(),ops=read(),scanf("%s",ch+1);
for(ll i=1;i<=n;i++) val[i]=ch[i]-'A'+1;
build(1,1,n); Mat res;
while(ops--){
opt=read();
if(opt&1) x=read(),scanf("%s",ch+1),val[x]=ch[1]-'A'+1,update(1,1,n,x);
else{
l=read(),r=read(),res=query(1,1,n,l,r),mul(res,o,res);
printf("%lld\n",(res.w[1][1]+res.w[1][2]+res.w[1][3])%mod);
}
}
exit(0);
}
D. 铺设道路
签到题,随便写.
D_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
#define ll long long int
#define lf double
#define ull unsigned ll
#define mp make_pair
#define lb lower_bound
#define ub upper_bound
#define lbt(x) ((x)&(-(x)))
#define Fill(x,y) memset(x,y,sizeof(x))
#define Copy(x,y) memcpy(x,y,sizeof(x))
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
auto read=[]()->ll{
ll w=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
return cit?w:(-w);
};
} using namespace BSS;
const ll N=3e5+21,mod=1e9+7;
ll m,n,t,maxn,minn;
ll le[N],ri[N],dep[N],lson[N],rson[N],stk[N],siz[N],lp[N],rp[N];
auto ksm=[](ll a,ll b,ll c)->ll{
ll w=1; for(a%=c;b;b>>=1,a=a*a%c) if(b&1) w=w*a%c;
return w%c;
};
void dfs(ll u,ll d){
siz[u]=1,t+=dep[u]-d,lp[le[u]]+=dep[u]-d,rp[ri[u]]+=dep[u]-d;
if(lson[u]) dfs(lson[u],dep[u]),siz[u]+=siz[lson[u]];
if(rson[u]) dfs(rson[u],dep[u]),siz[u]+=siz[rson[u]];
maxn=(maxn+(dep[u]-d)*siz[u]%mod*siz[u]%mod)%mod;
}
signed main(){
File(road);
n=read(); ll x,tail=0;
for(ll i=1;i<=n;i++) dep[i]=read();
for(ll i=1;i<=n;i++){
le[i]=i,ri[i]=i;
while(tail and dep[stk[tail]]>=dep[i]){
x=stk[tail--],lson[i]=x;
le[i]=min(le[i],le[x]),ri[x]=max(ri[x],i-1);
}
if(tail) rson[stk[tail]]=i; stk[++tail]=i;
}
while(tail) ri[stk[tail--]]=n;
dfs(stk[1],0);
for(ll i=1,j=1,ki,kj=rp[j];i<=n;i++){
ki=lp[i];
while(ki){
if(ki>=kj){
minn=(minn+(j-i+1)*(j-i+1)%mod*kj%mod)%mod;
ki-=kj,kj=rp[++j];
}
else{
minn=(minn+(j-i+1)*(j-i+1)%mod*ki%mod)%mod;
kj-=ki,ki=0;
}
}
}
printf("%lld\n%lld\n%lld\n",t,maxn,minn);
exit(0);
}
总结
之前的总结很多都穿插在每个题的题解里面,今天还是想再系统总结一下.
今天的题偏向套路,不是很难,自己好好思考不一定做不出来,但是今天状态依旧不太好,特别困,大概是因为 \(CSP\) 熬夜熬得太晚了.
考场上一个靠近正解的方法就是猜结论,然后试大样例,如果猜对了那就捧大杯了.
另外,今天的考试策略还算是偏好的,把自己困的时间稍微救了一下,没有特别烂.
临近 \(NOIP\),以后应该根据自己的成绩和做法慢慢调整下来一种灵活又稳固的策略了,毕竟 \(CSP-S\) 已经很惨了.