03-07考试总结

3月7日考试总结

结果3月8号才写


T1

得分情况 :

预计分数 : 40pts
实际得分 : 40pts
网上翻了自己的KMP板子写的 , 其实算没分

正解 :

由于我是看这道题AC代码理解的题解 , 所以下文的解释如果锅了 , 请务必联系我 出来挨打

考虑\(f(t)\)的实际意思 , 我(题)们(解)发现 :

一个串的\(f\)值即为有多少个非前缀子串前缀相同

证明的话可看到KMP的next(fail)数组相关
而题目要求的串的价值是它的每一个连续子串\(f\)的和
那么考虑一对相同的子串 , 设左边的串为\(x\) , 右边的为 \(y\)
那么我们发现 , 对于所有左端点\(x_l\) , 右端点在\(y_r\)原串的连续子串 , 这对\(x\),\(y\)有1点贡献
众所周知这样的串有\(S_{len} - y_r+1\)个 , 即\((x,y)\)的贡献为\(S_{len}-y_r+1\)
然后我们发现要求的是所有前缀的价值
那么就考虑每在当前串尾部加入一个字符的贡献
首先 , \(y_r < now\)\((x,y)\)都又产生了1点贡献 , 这里一发前缀和就好
然后考虑所有\(y_r = now\)\((x,y)\) , 这个的数量就是与当前串的后缀相同的串的数量
这玩意可以用SAM的parent树算 , 我觉得我写不清楚语文过差QAQ , 就具体见代码8
核(全)心(部)代码 :

#include<bits/stdc++.h>
#define r(a) (a)=read<int>()
#define rl(a) (a)=read<ll>()
#define MAXN(n) ((max##n)+10)
using namespace std;

typedef long long ll;
const int maxn=100000;
const int mod=998244353;

struct Sam{
	struct node{
		int link,len;
		int next[30];
	}st[MAXN(n)<<1];
	int sz,last;
	Sam(){init();}
	inline void init(){
		for(int i=1;i<=sz;i=-~i)st[i]=st[sz+1];
		sz=1;last=1;st[1].len=0;st[1].link=-1;
	}
	inline void insert(int c){
		int cur=++sz,p=last;
		st[cur].len=st[p].len+1;
		while(p!=-1&&(!st[p].next[c]))
			st[p].next[c]=cur,p=st[p].link;
		if(p==-1)st[cur].link=1;
		else{
			int q=st[p].next[c];
			if(st[q].len==st[p].len+1)st[cur].link=q;
			else{
				int clone=++sz;
				st[clone].len=st[p].len+1;st[clone].link=st[q].link;
				memcpy(st[clone].next,st[q].next,sizeof(st[q].next));
				while(p!=-1&&st[p].next[c]==q)
					st[p].next[c]=clone,p=st[p].link;
				st[q].link=st[cur].link=clone;
			}
		}last=cur;
	}
	inline void Build_tree();
}sam;

inline int Add(int a,int b){return (a%mod+b%mod)%mod;}
inline int Mul(int a,int b){return (1ll*(a%mod)*(b%mod)%mod);}

struct Graph{
	struct edge{int v,next;};
	edge e[MAXN(n)<<2];
	int n,tail,head[MAXN(n)<<1],Val[MAXN(n)<<1];
	int fa[MAXN(n)<<1],dep[MAXN(n)<<1],size[MAXN(n)<<1],son[MAXN(n)<<1];
	int top[MAXN(n)<<1],id[MAXN(n)<<1],Rid[MAXN(n)<<1],cnt;
	inline void add(int u,int v){e[++tail].next=head[u],head[u]=tail,e[tail].v=v;}
	void dfs1(int u,int Fa){
		fa[u]=Fa,dep[u]=dep[fa[u]]+1;size[u]=1;
		for(int i=head[u];i;i=e[i].next){
			int v=e[i].v;dfs1(v,u),size[u]+=size[v];
			if(size[v]>size[son[u]])son[u]=v;
		}
	}
	void dfs2(int u,int tFa){
		id[u]=++cnt,Rid[cnt]=u,top[u]=tFa;
		if(!son[u])return;
		dfs2(son[u],tFa);
		for(int i=head[u];i;i=e[i].next){
			int v=e[i].v;if(v!=son[u])dfs2(v,v);
		}
	}
	struct SGT{
		#define ls (rt<<1)
		#define rs (rt<<1|1)
		int det[MAXN(n)<<3],sum[MAXN(n)<<3],add[MAXN(n)<<3],*Val,*Rid;
		SGT(){
			memset(det,0,sizeof(det));
			memset(sum,0,sizeof(sum));
			memset(add,0,sizeof(add));
		}
		inline void push_up(int rt){sum[rt]=Add(sum[ls],sum[rs]);}
		inline void push_down(int rt){
			if(!add[rt])return;
			sum[ls]=Add(sum[ls],Mul(det[ls],add[rt])),add[ls]=Add(add[ls],add[rt]);
			sum[rs]=Add(sum[rs],Mul(det[rs],add[rt])),add[rs]=Add(add[rs],add[rt]);
			add[rt]=0;
		}
		inline void build(int rt,int l,int r){
			if(l==r){
				det[rt]=Val[Rid[l]];
				return;
			}
			int mid=(l+r)>>1;
			build(ls,l, mid );
			build(rs,mid+1,r);
			det[rt]=Add(det[ls],det[rs]);
		}
		inline void update(int rt,int l,int r,int L,int R){
			if(L<=l&&r<=R){
				sum[rt]=Add(sum[rt],det[rt]);
				add[rt]=Add(add[rt],1);return;
			}
			int mid=(l+r)>>1;push_down(rt);
			if(L<=mid)update(ls,l, mid ,L,R);
			if(R> mid)update(rs,mid+1,r,L,R);
			push_up(rt);
		}
		inline int query(int rt,int l,int r,int L,int R){
			if(L<=l&&r<=R){
				return sum[rt];
			}
			int mid=(l+r)>>1,ans=0;push_down(rt);
			if(L<=mid)ans=Add(ans,query(ls,l, mid ,L,R));
			if(R> mid)ans=Add(ans,query(rs,mid+1,r,L,R));
			return ans;
		}
		#undef ls
		#undef rs
	}T;
	void build_SGT(){
		dfs1(1,0);dfs2(1,1);
		T.Val=(*this).Val,T.Rid=(*this).Rid;
		T.build(1,1,n);
	}
	inline void update(int u){
		while(u){
			T.update(1,1,n,id[top[u]],id[u]);
			u=fa[top[u]];
		}return;
	}
	inline int query(int u){
		int ans=0;
		while(u){
			int res=T.query(1,1,n,id[top[u]],id[u]);
			ans=Add(ans,res);u=fa[top[u]];
		}return ans;
	}
}G;

int n;
char s[MAXN(n)];

int Sam_num[MAXN(n)];

template<class T>inline T read();

inline void Sam::Build_tree(){
	G.n=sz;
	for(int i=1;i<=sz;i=-~i){
		G.add(st[i].link,i);
		if(st[i].link!=-1)G.Val[i]=st[i].len-st[st[i].link].len;
	}return;
}

int main(){
	freopen("s.in","r",stdin);
	freopen("s.out","w",stdout);
	r(n);scanf("%s",s+1);
	for(int i=1;i<=n;i=-~i){
		Sam_num[i]=sam.sz+1;
		sam.insert(s[i]-'a'+1);
	}
	sam.Build_tree();
	G.build_SGT();
	int Sum=0,Ans=0;
	for(int i=1;i<=n;i=-~i){
		int Res=G.query(Sam_num[i]);
		Sum=Add(Sum,Res);Ans=Add(Ans,Sum);
		G.update(Sam_num[i]);printf("%d\n",Ans);
	}
	return 0;
}

template<class T>inline T read(){
	T sum=0,f=1;char k=getchar();
	for(;k< '0'||'9'< k;k=getchar())
		if(k=='-')f=-1;
	for(;'0'<=k&&k<='9';k=getchar())
		sum=(sum<<1)+(sum<<3)+(k^48);
	return sum*f;
}

太长了而且忘记压行了quq 麻烦将就一哈


T2

得分情况 :

预计分数 : 40pts
实际得分 : 10pts
像7号T3一样挂在了边界条件上
可笑的是我把自己之前考虑了边界的40pts代码删了
挂了30pts

正解 :

首先我(题)们(解)发现对于一个已知的序列\(a\) , 我们可以用网络流来判断是否合法
流程如下(摘自题解) :

S连向每一行 , 流量为\(a_i\) ; 每一列连向T , 流量为\(b_i\) ; 所有行向所有列连边 , 流量为1 ;
如果\(\sum_{i=1}^{n} a_i=\sum_{i=1}^{m} b=MaxFlow\),则合法;否则不合法

好了口胡一哈这玩意为啥是对的 :
首先 , 我们有 : \(\sum_{i=1}^{n} a_i=\sum_{i=1}^{m} b=MaxFlow\) , 这说明\(a_i\)\(b_i\)都跑满了
然后发现每行每列都连了流量1的边 , 那么这之中的一些边也是满的
回到原来的图上 , 使得 : \(bok_{ij}=1 \; [edge_{i->j} \; is \; full]\)
看得出来这样的棋子的放置一定合法

好那么我们可以把这个原题吃掉了 , 现在要求的是有多少种合法序列\(a\) , 满足上面那个情况
发现也不好算 , 于是转成最小割 : \(min \{ prea_x + preb_y + (n-x)*(m-y)\}\)
\(prea\;preb\)表示\(a\;b\)排序后的前缀和
然后发现可以靠这玩意求出\(prea_x\)的下界 , 设为\(low_x\)
然后DP :

\(f_{i,j,k}\)表示在序列\(a\)中填了\(i\)个数 , 这\(i\)个数最大为\(j\)且和为\(k\)的方案数
则当 : \(x \in \left[ 0,n-i \right] \; \& \& \; k + \left( j+1 \right) * x \geq low_{i+x}\)
有 : \(f_{i+x,j+1,k+(j+1)*x}+=f_{i,j,k}*C_{n-i}^{x}\)

解释 : 在剩下的空位之中放入\(x\)\(j+1\) , 方案数\(*C_{n-i}^{x}\)
对于后继状态 , 它比之前多放了\(x\)个数 , 最大值变为\(j+1\) , 和多了\(x*(j+1)\)
于是就有了上面的那个方程
剩下的就是跑这玩意了 , 细节见代码

    for(int i=0;i<=n;i=-~i){
		for(int j=0;j<=m;j=-~j)
			lowa[i]=max(lowa[i],sumb-preb[j]-(n-i)*(m-j));
		if(lowa[i]==0)f[i][0][0]=C[n][i];
	}
	for(int i=0;i<=n;i=-~i){
		for(int j=0;j<=m;j=-~j){
			for(int k=0;k<=n*m;k=-~k){
				if(!f[i][j][k])continue;
				for(int x=0;x<=n-i;x=-~x)
					if(lowa[i+x]<=k+(j+1)*x&&k+(j+1)*x<=sumb)
						f[i+x][j+1][k+(j+1)*x]=(f[i+x][j+1][k+(j+1)*x]
						+(1ll*f[i][j][k]*C[n-i][x]%mod))%mod;
			}
		}
	}

T3

得分情况 :

预计分数 : 0pts
实际得分 : 0pts
写T240分去了 , 没时间了

正解 :

因为不会NTT所以暂且不考虑改这个

posted @ 2019-03-08 15:09  dnktm  阅读(209)  评论(2编辑  收藏  举报