[Comet OJ - Contest #6 ]简要题解?

UPD 7.11 后两题的真正题解补完了

图游戏

n-1-m的奇偶性

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

int n,m;

int main() {
	scanf("%d%d",&n,&m);
	if ((n-1-m)&1) puts("Illyasviel");
	else puts("Star-dust");
	return 0;
}

双倍快乐

暴力枚举结尾,n^3

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

const int N=505;

int f[2][N][N],n,a[N];

int main() {
	scanf("%d",&n);
	fo(i,1,n) scanf("%d",&a[i]);
	memset(f,128,sizeof(f));
	f[0][0][0]=0;
	fo(i,1,n) {
		int t=i&1;
		fo(j,0,i) fo(k,0,i) f[t][j][k]=f[t^1][j][k];
		fo(j,0,i-1)
			fo(k,0,i-1) {
				if (a[i]>=a[j]) f[t][i][k]=max(f[t][i][k],f[t^1][j][k]+a[i]);
				if (a[i]>=a[k]) f[t][j][i]=max(f[t][j][i],f[t^1][j][k]+a[i]);
			}
	}
	int ans=0;
	fo(i,1,n) fo(j,1,n) ans=max(ans,f[n&1][i][j]);
	printf("%d\n",ans);
	return 0;
}

一道树题

连通块数=点-边
考虑一个点会对哪些区间贡献

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define pb(a) push_back(a)
using namespace std;

typedef long long ll;

int read() {
	char ch;
	for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
	int x=ch-'0';
	for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x;
}

const int N=2e5+5;

int n;
vector<int> vec[N];

ll calc(ll x) {return x*(x+1)/2;}

int main() {
	n=read();
	fo(i,1,n-1) {
		int x=read(),y=read();
		vec[x].pb(i);vec[y].pb(i);
	}
	ll sum=(ll)n*(n-1)/2,ans=0;
	fo(i,1,n) {
		int la=0;ans+=sum;
		for(int j=0;j<vec[i].size();j++) {
			ans-=calc(vec[i][j]-la-1);
			la=vec[i][j];
		}
		ans-=calc(n-1-la);
	}
	fo(i,1,n-1) ans-=(ll)i*(n-i);
	printf("%lld\n",ans);
	return 0;
}

另一道树题

考虑一开始每个点都有一个棋子,每一轮把每个棋子移到其父亲处
设C[i][j]表示i轮后点j上的棋子数,答案为 ∑ ∏ j = 1 n ( C [ i ] [ j ] + 1 ) − N − 1 \sum\prod_{j=1}^{n}(C[i][j]+1)-N-1 j=1n(C[i][j]+1)N1
考虑用长链剖分维护C[i],注意到C[i]的值只会产生O(n)次修改,每次修改时处理贡献即可

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=lst[a];i;i=nxt[i])
using namespace std;

typedef long long ll;

int read() {
	char ch;
	for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
	int x=ch-'0';
	for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x;
}

const int N=4e5+5,Mo=998244353;

int pwr(int x,int y) {
	int z=1;
	for(;y;y>>=1,x=(ll)x*x%Mo)
		if (y&1) z=(ll)z*x%Mo;
	return z;
}

int t[N<<1],nxt[N<<1],lst[N],l;
void add(int x,int y) {t[++l]=y;nxt[l]=lst[x];lst[x]=l;}

int n,dep[N],mx[N],f[N],an[N],son[N],dfn[N],id[N],la[N],cnt[N],tot;

void dfs(int x) {
	mx[x]=x;
	rep(i,x) {
		dep[t[i]]=dep[x]+1;dfs(t[i]);
		if (dep[mx[t[i]]]>dep[mx[x]]) mx[x]=mx[t[i]],son[x]=t[i];
	}
}

void make(int x) {
	dfn[x]=++tot;
	if (!son[x]) return;
	make(son[x]);
	rep(i,x) if (t[i]!=son[x]) make(t[i]);
}

void inc(int &x,int y) {x=x+y>=Mo?x+y-Mo:x+y;}

void mdf(int l,int r,int c) {an[l]=(ll)an[l]*c%Mo;an[r+1]=(ll)an[r+1]*pwr(c,Mo-2)%Mo;}

void dp(int x) {
	rep(i,x) dp(t[i]);
	f[dfn[x]]=1;la[dfn[x]]=0;int m=0;
	rep(i,x) if (t[i]!=son[x]) m=max(m,dep[mx[t[i]]]-dep[t[i]]+1);
	fo(j,1,m) {
		mdf(la[dfn[x]+j],j-1,f[dfn[x]+j]+1);
		la[dfn[x]+j]=j;
	}
	rep(i,x) 
		if (t[i]!=son[x]) 
			fo(j,0,dep[mx[t[i]]]-dep[t[i]]) {
				inc(f[dfn[x]+j+1],f[dfn[t[i]]+j]);
				mdf(la[dfn[t[i]]+j],j,f[dfn[t[i]]+j]+1);
			}
	
}

int main() {
	n=read();
	fo(i,2,n) {
		int x=read();
		add(x,i);
	}
	fo(i,0,n) an[i]=1;
	dfs(1);make(1);dp(1);
	fo(i,1,n) cnt[dep[i]]++;
	fo(i,0,dep[mx[1]]) mdf(la[1+i],i,f[1+i]+1);
	fo(i,1,dep[mx[1]]) an[i]=(ll)an[i]*an[i-1]%Mo;
	int sum=0;
	fo(i,0,dep[mx[1]]) {
		sum+=cnt[i];
		an[i]=(ll)an[i]*pwr(cnt[i]+1,Mo-2)%Mo;
		an[i]=(ll)an[i]*(sum+1)%Mo;
	}
	int ans=0;
	fo(i,0,dep[mx[1]]) {
		(ans+=an[i])%=Mo;
		(ans-=n+1)%=Mo;
	}
	printf("%d\n",(ans+Mo)%Mo);
	return 0;
}

字符串

若后缀i和j的lcp为l
贡献为 ∑ k = 0 l − 1 k ∗ ( i + j − 2 ∗ k + 1 ) + l ∗ ( i − l + 1 ) ∗ ( j − l + 1 ) \sum_{k=0}^{l-1}k*(i+j-2*k+1)+l*(i-l+1)*(j-l+1) k=0l1k(i+j2k+1)+l(il+1)(jl+1)
拆开来维护一坨东西,用链剖维护前缀的差分即可做到O(n log^2 n)
TLE到飞起

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef long long ll;

const int N=4e5+5,Mo=998244353;

void write(ll x) {
	if (!x) {putchar('0');return;}
	char ch[20];int tot=0;
	for(;x;x/=10) ch[++tot]=x%10+'0';
	fd(i,tot,1) putchar(ch[i]);
}

int n,lst,pos[N],fa[N],tot;
char st[N];

struct SAM{int son[26],len,pr;}sam[N];

int extend(int x,int p) {
	int np=++tot;sam[np].len=sam[p].len+1;
	while (p&&!sam[p].son[x]) sam[p].son[x]=np,p=sam[p].pr;
	if (!p) sam[np].pr=1;else {
		int q=sam[p].son[x];
		if (sam[q].len==sam[p].len+1) sam[np].pr=q;
		else {
			int nq=++tot;
			sam[nq]=sam[q];
			sam[nq].len=sam[p].len+1;
			sam[q].pr=sam[np].pr=nq;
			while (p&&sam[p].son[x]==q) sam[p].son[x]=nq,p=sam[p].pr;
		}
	}
	return np;
}

ll an[N];

vector<int> son[N];
int s[N],sz[N],top[N],dfn[N],id[N],tmp;

void dfs(int x) {
	sz[x]=1;int k=0;
	for(int z:son[x]) {
		dfs(z);
		sz[x]+=sz[z];
		if (sz[z]>k) k=sz[z],s[x]=z;
	}
}

void make(int x,int y) {
	top[x]=y;dfn[x]=++tmp;id[tmp]=x;
	if (!s[x]) return;
	make(s[x],y);
	for(int z:son[x]) {
		if (z==s[x]) continue;
		make(z,z);
	}
}

// 0 (t+1)*∑l
// 1 ∑l
// 2 ∑l*l
// 3 (t+1)*l
// 4 l*l
// 5 (t+1)*l*l
// 6 l*l*l

int s1[N],s2[N],p1[N],p2[N],p3[N];
ll tr[N<<2][7],tg_t[N<<2],tg_c[N<<2],a[7];

void upd(int v) {fo(i,0,6) tr[v][i]=(tr[v<<1][i]+tr[v<<1|1][i])%Mo;}

void apply(int v,int l,int r,ll t,ll c) {
	(tr[v][0]+=t*(s1[sam[id[r]].len]-s1[sam[fa[id[l]]].len]))%=Mo;
	(tr[v][1]+=c*(s1[sam[id[r]].len]-s1[sam[fa[id[l]]].len]))%=Mo;
	(tr[v][2]+=c*(s2[sam[id[r]].len]-s2[sam[fa[id[l]]].len]))%=Mo;
	(tr[v][3]+=t*(p1[sam[id[r]].len]-p1[sam[fa[id[l]]].len]))%=Mo;
	(tr[v][4]+=c*(p2[sam[id[r]].len]-p2[sam[fa[id[l]]].len]))%=Mo;
	(tr[v][5]+=t*(p2[sam[id[r]].len]-p2[sam[fa[id[l]]].len]))%=Mo;
	(tr[v][6]+=c*(p3[sam[id[r]].len]-p3[sam[fa[id[l]]].len]))%=Mo;
	(tg_t[v]+=t)%=Mo;tg_c[v]+=c;
}

void down(int v,int l,int r) {
	if (tg_t[v]||tg_c[v]) {
		int mid=l+r>>1;
		apply(v<<1,l,mid,tg_t[v],tg_c[v]);
		apply(v<<1|1,mid+1,r,tg_t[v],tg_c[v]);
		tg_t[v]=0;tg_c[v]=0;
	}
}

void modify(int v,int l,int r,int x,int y,int z) {
	if (x<=l&&r<=y) {apply(v,l,r,z,1);return;}
	int mid=l+r>>1;down(v,l,r);
	if (x<=mid) modify(v<<1,l,mid,x,y,z);
	if (y>mid) modify(v<<1|1,mid+1,r,x,y,z);
	upd(v);
}

void query(int v,int l,int r,int x,int y) {
	if (x<=l&&r<=y) {
		fo(i,0,6) a[i]+=tr[v][i];
		return;
	}
	int mid=l+r>>1;down(v,l,r);
	if (x<=mid) query(v<<1,l,mid,x,y);
	if (y>mid) query(v<<1|1,mid+1,r,x,y);
	upd(v);
}

void Mdf(int x,int l) {for(;x;x=fa[top[x]]) modify(1,1,tot,dfn[top[x]],dfn[x],l);}

void Que(int x) {
	fo(i,0,6) a[i]=0;
	for(;x;x=fa[top[x]]) query(1,1,tot,dfn[top[x]],dfn[x]);
}

int calc(int l) {
	int ret=l;
	(ret+=(ll)(2*l+1)*l*(l-1)/2%Mo)%=Mo;
	(ret-=(ll)(l-1)*l*(2*l-1)/3%Mo)%=Mo;
	/*ret+=(s+1)*(t+1)*l;
	ret-=(s+1)*l*l;
	ret-=(t+1)*l*l;
	ret+=l*l*l;*/
	return ret;
}

int main() {
	scanf("%s",st+1);n=strlen(st+1);
	reverse(st+1,st+n+1);
	lst=tot=1;
	fo(i,1,n) pos[i]=lst=extend(st[i]-'a',lst);
	fo(i,2,tot) fa[i]=sam[i].pr,son[fa[i]].push_back(i);
	fo(i,1,n) s1[i]=s1[i-1]+i-1;
	fo(i,1,n) s2[i]=(s2[i-1]+(ll)(i-1)*(i-1)%Mo)%Mo;
	fo(i,1,n) p1[i]=i,p2[i]=(ll)i*i%Mo,p3[i]=(ll)i*i*i%Mo;
	dfs(1);make(1,1);
	fo(i,1,n) {
		Que(pos[i]);
		fo(i,0,6) a[i]%=Mo;
		an[i]+=a[0];
		an[i]+=(ll)a[1]*i;
		an[i]-=a[2]*2;
		an[i]+=(ll)a[3]*(i+1);
		an[i]-=(ll)a[4]*(i+1);
		an[i]-=a[5];an[i]+=a[6];
		an[i]=(an[i]*2%Mo+calc(i))%Mo;
		Mdf(pos[i],i+1);
	}
	fo(i,1,n) an[i]+=an[i-1];
	fd(i,n,1) printf("%lld ",(an[i]+Mo)%Mo);
	return 0;
}

一个log的做法在这里

permutation

OEIS A111111
题解在这里

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值