考完后的心情:


T1 「MZOI2020」快速班号变换

简单DP题,没处理边界,\(100 \to 70\)

\(f_{i,j}\) 表示使得 \(a\) 串前 \(i\) 位变换到 \(b\) 串前 \(j\) 位的最小花费,则有转移:

  • \(a_i\) 变成 \(b_j\) ,则 \(f_{i,j}=f_{i-1,j-1}+{\rm{dist}}(a_i,b_j)\)
  • 删除 \(a_i\) ,则 \(f_{i,j}=f_{i-1,j}+a_i\)
  • 插入 \(b_j\) ,则 \(f_{i,j}=f_{i,j-1}+b_j\)

\(\text{Code}:\)

memset(f,0x3f,sizeof(f));
f[0][0]=0;
for(int i=0;i<=n;++i)
	for(int j=0;j<=m;++j)
	{
		if(i>=1&&j>=1) f[i][j]=min(f[i][j],f[i-1][j-1]+dis(a[i],b[j]));
		if(j>=1) f[i][j]=min(f[i][j],f[i][j-1]+b[j]);
		if(i>=1) f[i][j]=min(f[i][j],f[i-1][j]+a[i]);
	}
printf("%d\n",f[n][m]);

T2 「MZOI2020」魔导书

考试的时候想了个错的贪心,理所应当地爆零了。

假如已经选了一些书叠到了一起,此时上面最多能再叠 \(up\) 本书,对于剩下的书分类讨论:

  • 若存在一本书,使得它加入后不会改变 \(up\) ,即 \(b\geq up-1\) ,选择这样的书中 \(a\) 最大的加入。
  • 若不存在上述的书,则选择一本 \(b\) 尽量大的书,因为这样能使之后能加入的书尽量多。

按这个思路贪心即可。

\(\text{Code}:\)

#include <cstring>
#include <cstdio>
#include <algorithm>
#define maxn 1000005
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
const int mod=910666;

template <typename T>
inline void read(T &x)
{
	x=0;T f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	x*=f;
}

struct Book
{
	int a,b;
	Book(int a,int b):a(a),b(b){}
	Book(){}
	inline bool operator < (const Book &T)const
	{
		if(b==T.b) return a<T.a;
		return b<T.b;
	}
}book[maxn];

int n;

struct Segment_Tree
{
	struct node
	{
		int l,r,Max;
	}tree[maxn<<2];
	#define ls (p<<1)
	#define rs (p<<1|1)
	int a[maxn];
	inline int max(const int &x,const int &y)
	{
		return a[x]>a[y]?x:y;
	}
	inline void maintain(int p)
	{
		tree[p].Max=max(tree[ls].Max,tree[rs].Max);
	}
	inline void build(int p,int l,int r)
	{
		tree[p]=(node){l,r,0};
		if(l==r) return void(tree[p].Max=l);
		int mid=(l+r)>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
		maintain(p);
	}
	inline void update(int p,int ps)
	{
		int l=tree[p].l,r=tree[p].r;
		if(l==r) return void(tree[p].Max=0);
		int mid=(l+r)>>1;
		if(ps<=mid) update(ls,ps);
		else update(rs,ps);
		maintain(p);
	}
	inline int query(int p,int L,int R)
	{
		int l=tree[p].l,r=tree[p].r;
		if(L<=l&&r<=R) return tree[p].Max;
		int mid=(l+r)>>1,ans=0;
		if(L<=mid) ans=max(ans,query(ls,L,R));
		if(R>mid) ans=max(ans,query(rs,L,R));
		return ans;
	}
}sta,stb;

int main()
{
	// freopen("B.in","r",stdin);
	read(n);
	for(int i=1;i<=n;++i) read(book[i].a);
	for(int i=1;i<=n;++i) read(book[i].b);
	sort(book+1,book+n+1);
	for(int i=1;i<=n;++i) sta.a[i]=book[i].a,stb.a[i]=book[i].b;
	sta.build(1,1,n);sta.update(1,n);
	stb.build(1,1,n);stb.update(1,n);
	int up=book[n].b,ans=book[n].a%mod,cnt=1;
	while(up>0&&cnt<n)
	{
		--up;
		int p=lower_bound(book+1,book+n+1,Book(-1,up))-book;
		int now=sta.query(1,p,n);
		if(now) ans=(ans+book[now].a)%mod;
		else
		{
			now=stb.tree[1].Max;
			up=book[now].b;
			ans=(ans+book[now].a)%mod;
		}
		++cnt;
		sta.update(1,now);
		stb.update(1,now);
	}
	printf("%d\n", ans);
	return 0;
}

T3 「MZOI2020」遗传因子

考场上想出正解了,然后因为数组开小,\(100 \to 30\)

建一个AC自动机,在fail树上统计答案即可。

感觉这东西挺套路的。

\(\text{Code}:\)

#include <cstring>
#include <cstdio>
#include <string>
#include <algorithm>
#include <queue>
#define maxn 10005
#define maxnode 1000005
using namespace std;

template <typename T>
inline void read(T &x)
{
	x=0;T f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	x*=f;
}

struct edge
{
	int u,v,next;
}e[maxnode];

int head[maxnode],k;

inline void add(int u,int v)
{
	e[k]=(edge){u,v,head[u]};
	head[u]=k++;
}

int n,L[maxn],R[maxn];
char s[maxnode],t[maxnode];

int ch[maxnode][30],tree[maxnode][30],tot,fail[maxnode],book[maxnode];

inline void insert(int id)
{
	int u=0;
	for(int i=L[id];i<=R[id];++i)
	{
		int c=s[i]-'a'+1;
		if(!ch[u][c]) ch[u][c]=tree[u][c]=++tot;
		u=ch[u][c];
	}
	book[u]=id;
}

inline void get_fail()
{
	queue<int> q;
	memset(fail,-1,sizeof(fail));
	for(int i=1;i<=26;++i)
		if(ch[0][i]) q.push(ch[0][i]),fail[ch[0][i]]=0;
	while(!q.empty())
	{
		int u=q.front();q.pop();
		for(int i=1;i<=26;++i)
			if(ch[u][i])
			{
				fail[ch[u][i]]=ch[fail[u]][i];
				q.push(ch[u][i]);
			}
			else ch[u][i]=ch[fail[u]][i];
	}
	for(int i=1;i<=tot;++i) if(~fail[i])
		add(fail[i],i);
}

int f[maxnode],g[maxnode];

inline void dfs(int u)
{
	if(book[u])
	{
		f[u]+=R[book[u]]-L[book[u]]+1;
		++g[u];
	}
	for(int i=head[u];~i;i=e[i].next)
	{
		int v=e[i].v;
		f[v]+=f[u];
		g[v]+=g[u];
		dfs(v);
	}
}

int Ans[maxn],Sum;

inline void Get_Ans(int u,int sum,int cnt)
{
	if(book[u])
	{
		Ans[book[u]]=(R[book[u]]-L[book[u]]+1)*cnt-sum;
		Sum+=Ans[book[u]];
	}
	for(int i=1;i<=26;++i)
	{
		int v=tree[u][i];
		if(!v) continue;
		Get_Ans(v,sum+f[v],cnt+g[v]);
	}
}

int main()
{
	// freopen("C.in","r",stdin);
	read(n);
	for(int i=1;i<=n;++i)
	{
		scanf(" %s",t+1);
		L[i]=R[i-1]+1;
		R[i]=L[i]+strlen(t+1)-1;
		strcat(s+L[i],t+1);
		insert(i);
	}
	memset(head,-1,sizeof(head));
	get_fail();
	dfs(0);
	Get_Ans(0,f[0],g[0]);
	printf("%d\n",Sum);
	for(int i=1;i<=n;++i)
		printf("%d\n",Ans[i]);
	return 0;
}


死亡原因

  1. DP没处理边界。
  2. 数组开小。

我退役罢