21杭电多校第四场

A

签到题,显然只有当所有系数都\(C=0\)才能收敛,判一下即可

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
char s[110];int n,ans,l,r;
int main()
{
	rep(T,1,read()) 
	{
		scanf("%s",s+1);n=strlen(s+1);l=1;ans=1;
		rep(i,1,n+1) if(i==n+1||s[i]=='+')
		{
			int flg=1;r=i-1;
			if(s[l]!='0') {ans=0;break;}
			l=i+1;
		}
		puts(ans?"YES":"NO");
	}
}

B

签到题,每次\(dfs\)开一个数组记录目前每种颜色的个数,进\(+1\),出\(-1\)

这样得到所有\(a_{i,j}\) 累加即可

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 2200
#define MOD1 1000000007
#define MOD2 1000000009
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls1(a,b) (a+b)%MOD1
#define pls2(a,b) (a+b)%MOD2
#define mns(a,b) (a-b+MOD)%MOD
#define mul1(a,b) (1LL*(a)*(b))%MOD1
#define mul2(a,b) (1LL*(a)*(b))%MOD2
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int pw[MAXN][2],n,col[MAXN],nxt[MAXN<<1],fst[MAXN],to[MAXN<<1],res[MAXN],cnt,ans1,ans2,tot;
void add(int u,int v){nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v;}
void dfs(int x,int pa)
{
	res[col[x]]++;if(res[col[x]]==1) tot++;
	ans1=pls1(ans1,mul1(pw[x-1][0],tot));
	ans2=pls2(ans2,mul2(pw[x-1][1],tot));
	ren if(to[i]^pa) dfs(to[i],x);
	res[col[x]]--;if(res[col[x]]==0) tot--;
}
int main()
{
	pw[0][0]=pw[0][1]=1;rep(T,1,read())
	{
		n=read();int a;
		rep(i,2,n) {a=read(),add(a,i),add(i,a);}
		rep(i,1,n) col[i]=read(),
			pw[i][0]=mul1(pw[i-1][0],19560929),pw[i][1]=mul2(pw[i-1][1],19560929);
		rep(i,1,n) {dfs(i,0);printf("%d %d\n",ans1,ans2);ans1=ans2=0;}
		rep(i,1,n) fst[i]=0;tot=cnt=0;
	}
}

C

对所有长度为\(n\)的串,设\(f(i)\)表示最小周期为\(i\)的串的个数,则\(ans=\sum\limits_{i=1}^nf(i)\lfloor\frac{n}{i}\rfloor\)

有一个朴素的求\(f(i)\)的想法即为\(f(n)=2^n-\sum\limits_{i|n,i<n}f(i)\)

这个式子本身是没有什么问题的,但是在计算\(ans\)的时候,对于\(\lfloor\frac{n}{i}\rfloor=1\)的情况,可能会计算重复

例如\(11011\)这个串,在最小周期为\(3,4,5\)时即作为\(110\ 11\)\(1101\ 1\)\(11011\)分别被计算了一次

\(\lfloor\frac{n}{i}\rfloor\ge 2\)时,则没有这个问题,因为若有不同的重复一定会出现更小的循环节

因此考虑吧\(\lfloor\frac{n}{i}\rfloor=1\)\(i>\lfloor\frac{n}{2}\rfloor\)的部分单独拿出来计算,即

\(ans=\sum\limits_{i=1}^{\lfloor\frac{n}{2}\rfloor}f(i)\lfloor\frac{n}{i}\rfloor+1\cdot (2^n-\sum\limits_{i=1}^{\lfloor\frac{n}{2}\rfloor}f(i))\\ \quad\ \ \ =2^n+\sum\limits_{i=1}^{\lfloor\frac{n}{2}\rfloor}(\lfloor\frac{n}{i}\rfloor-1)f(i)\)

是一个数论分块的形式,考虑如何快速计算\(f(i)\)的前缀和

\(f(n)=2^n-\sum\limits_{i|n,i<n}f(i)\)移项后得\(f*1=2^n\),已经是杜教筛形式了,直接套

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 1001001
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b))%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int f[MAXN];const int lim=1e6;
int qp(int x,int t,int res=1)
{
	for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);
	return res;
}
void mem(int n=lim)
{
	int pw=1;rep(i,1,n)
	{
		pw=mul(pw,2);f[i]=mns(pw,f[i]);
		for(int j=i*2;j<=n;j+=i) f[j]=pls(f[j],f[i]);
	}
	rep(i,1,n) f[i]=pls(f[i-1],f[i]);
}
unordered_map<int,int> ansf;
int sum(int n)
{
	if(n<=lim) return f[n];if(ansf[n]) return ansf[n];
	ll res=0;for(int l=2,r;l<=n;l=r+1)
		r=n/(n/l),res=pls(res,mul(sum(n/l),r-l+1));
	res=mns(qp(2,n+1)-2,res);return ansf[n]=res;
}
int solve(int n,int res=0)
{
    for(int l=1,r;l<=(n/2);l=r+1)
		r=n/(n/l),res=pls(res,mul((n/l-1),mns(sum(r),sum(l-1))));
    return pls(res,qp(2,n)); 
}
int main()
{
	mem();rep(T,1,read())
	{
		int n=read();printf("%d\n",pls(MOD,solve(n)));
	}
}

D

考虑二分答案,然后\(check\)

对于后缀树,因为每个节点的\(right\)集合中对应的串是连续的

处理一下原数组的前缀和,对每个节点再套一个二分,在范围内找到有多少个满足条件的串

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 200100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,a[30],sum[MAXN],rt,las,tot,mxl[MAXN],fa[MAXN],pos[MAXN];ll m;
int tr[MAXN][26],res;char s[MAXN];
void extend(int c,int id)
{
    int p=las,np=las=++tot;mxl[np]=mxl[p]+1;pos[np]=id;
    for(;p&&!tr[p][c];p=fa[p]) tr[p][c]=np;
    if(!p) {fa[np]=rt;return ;}int q=tr[p][c];
    if(mxl[q]==mxl[p]+1) {fa[np]=q;return ;}
    int nq=++tot;mxl[nq]=mxl[p]+1;pos[nq]=pos[q];
    memcpy(tr[nq],tr[q],sizeof(tr[nq]));
    fa[nq]=fa[q],fa[np]=fa[q]=nq;
    for(;p&&tr[p][c]==q;p=fa[p]) tr[p][c]=nq;
}
int cheq(int x,ll cnt=0)
{
	rep(i,2,tot)
	{
		int l=pos[i]-mxl[i]+1,r=pos[i]-mxl[fa[i]],res=-1,R=r;
		for(int mid=l+r>>1;l<=r;mid=l+r>>1)
			if(sum[pos[i]]-sum[mid-1]<=x) res=mid,r=mid-1;
			else l=mid+1;
		if(~res) cnt+=R-res+1;
	}
	return cnt>=m;
}
int main()
{
	rep(T,1,read())
	{
		rt=las=tot=1;n=read();scanf("%lld",&m);scanf("%s",s+1);
		rep(i,0,25) a[i]=read();res=-1;
		rep(i,1,n) {extend(s[i]-'a',i);sum[i]=sum[i-1]+a[s[i]-'a'];}
		for(int l=1,r=sum[n],mid=l+r>>1;l<=r;mid=l+r>>1)
			if(cheq(mid)) res=mid,r=mid-1;
			else l=mid+1;
		printf("%d\n",res);
		rep(i,1,tot) {fa[i]=0;rep(j,0,25) tr[i][j]=0;}
	}
}

E

据说可以\(segment\ tree\ beats\)做 学完了就来(

F

不知道是什么的奇怪算法 弃了

G

朴素的做法就是对每个点\(a_i\)前面小于\(a_i\)的数做一个单调栈

\(f_i=\sum f_j\)其中\(j\)为单调栈上的点,若\(a_i\)为一个极小值则\(f_i=1\)

可以使用线段树维护一段区间的单调栈,这样按照权值升序在线段树上修改,每次查询即可

因为合并两个区间的单调栈是从右向左合并的,因此对每个区间肯定要记录的是左端点\(mx\)和单调栈上\(sum=\sum f_i\)

考虑合并的时候,相当于在左端点里要二分找到最靠右符合条件的右端点

定义\(query(l,r,lim)\)表示在\([l,r]\)区间内查询所有\(>lim\)的数组成的单调栈的\(\sum f_i\)

显然每次实际查询即为\(f_i=query(1,i,0)\)

在合并时,若当前\(lim\)大于右子树最大值,则右子树可以被扔掉,否则答案为左子树全部的答案加右子树的部分答案

考虑维护\(left_{ans}\)表示每个区间的\(query(l,mid,rmx)\)即左子树的全部答案

这样可以将查询的复杂度稳定在\(nlog^2\)

需要注意每次查询的时候需要记录一下当前靠右部分的左端点\(mx\)才能进行符合条件的左区间查询

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,g[MAXN],ans,f[MAXN],p[MAXN];
struct node
{
	int sum,mx,lft;
	void mem(){sum=0,mx=-1,lft=0;}
}tr[MAXN<<2];
void build(int k,int l,int r)
{
	tr[k].mem();if(l==r) return ;int mid=l+r>>1;
	build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
int nmx;
int query(int k,int l,int r,int a,int b,int w)
{
	if(tr[k].mx<w) return 0;
	if(l==r) {nmx=max(w,tr[k].mx);return tr[k].mx>w?tr[k].sum:0;}
	int mid=l+r>>1,res=0;
	if(a<=l&&r<=b)
	{
		if(w>tr[k<<1|1].mx) return query(k<<1,l,mid,a,b,w);
		res=pls(tr[k].lft,query(k<<1|1,mid+1,r,a,b,w));
		nmx=max(nmx,tr[k].mx);return res;
	}
	if(b<=mid) return query(k<<1,l,mid,a,b,w);
	else if(a>mid) return query(k<<1|1,mid+1,r,a,b,w);
	else
	{
		res=query(k<<1|1,mid+1,r,a,b,w);
		res=pls(res,query(k<<1,l,mid,a,b,nmx));
		return res;
	}
}
void mdf(int k,int l,int r,int x,int w)
{
	if(l==r) {tr[k].mx=w,tr[k].sum=f[w];return ;}int mid=l+r>>1;
	x<=mid?mdf(k<<1,l,mid,x,w):mdf(k<<1|1,mid+1,r,x,w);
	tr[k].lft=query(k<<1,l,mid,l,mid,tr[k<<1|1].mx);
	tr[k].sum=pls(tr[k].lft,tr[k<<1|1].sum);
	tr[k].mx=max(tr[k<<1].mx,tr[k<<1|1].mx);
}
int main()
{
	rep(T,1,read())
	{
		n=read();rep(i,1,n) g[i]=read(),p[g[i]]=i;
		build(1,1,n);
		rep(i,1,n)
		{
			nmx=0;f[i]=query(1,1,n,1,p[i],0);
			if(!f[i]) f[i]=1;mdf(1,1,n,p[i],i);
		}
		printf("%d\n",query(1,1,n,1,n,0));
	}
}

H

开一个\(vector\)表示当前这一行能走到哪些区间

向下一行转移的时候由于障碍物 这个区间可能分裂/删除/端点右移

模拟一下即可

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define x first
#define y second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,k;ll res;
vector<int> v[MAXN];
vector<pii> ans,tmp;
int main()
{
	rep(T,1,read())
	{
		n=read(),m=read(),k=read();int a,b;res=0;
		rep(i,1,n) v[i].clear();ans.clear();
		rep(i,1,k) a=read(),b=read(),v[a].pb(b);
		rep(i,1,n) if(v[i].size()) sort(v[i].begin(),v[i].end());
		if(!v[1].size()) {ans.pb({1,m});res+=m;}
		else {ans.pb({1,v[1][0]-1});res+=v[1][0]-1;}
		rep(i,2,n) 
		{
			vector<int>::iterator it;
			v[i].pb(m+1);int L=0,l,r;
			it=v[i].begin();
			//cout<<"id: "<<i<<" "<<res<<endl;
			for(auto t:ans)
			{
				//cout<<"t: "<<t.x<<" "<<t.y<<endl;
				bg:;
				l=max(t.x,L),r=t.y;if(l>r) continue;
				while((*it)<l&&it!=v[i].end()) it++;
				while((*it)==l&&it!=v[i].end()&&l<=r) l++,it++;
				if(l==r+1) continue;
				//cout<<l<<" "<<(*it)<<endl;
				tmp.pb({l,(*it)-1});L=(*it)+1;goto bg;
			}
			ans.clear();
			for(auto t:tmp) {res+=t.y-t.x+1;ans.pb(t);}
			tmp.clear();
		}
		printf("%lld\n",res);
	}
}

I

判断一下每一列是否非空,找到一些连续非空的区间

若区间个数\(>7\),则一定均为汉字产生,合并前面的若干个即可

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int n=30,m=100;
pii a[15];int tot;
char s[35][110];
int cheq(int x)
{
	rep(i,1,n) if(s[i][x]=='#') return 1;
	return 0;
}
int main()
{
	rep(T,1,read())
	{
		rep(i,1,n) scanf("%s",s[i]+1);tot=0;
		rep(i,1,m) if(cheq(i))
		{
			int j=i+1;while(cheq(j)) j++;
			j--;a[++tot]={i,j};i=j+1;
		}
		printf("Case #%d:\n",T);
		printf("%d %d\n",a[1].fi,a[tot-6].se);
		rep(i,tot-5,tot) printf("%d %d\n",a[i].fi,a[i].se);
	}
}

J

又是奇怪的算法 弃了

K

莫队 咕了

posted @ 2021-07-30 19:33  jack_yyc  阅读(28)  评论(0编辑  收藏  举报