考完后的心情:
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;
}
死亡原因
- DP没处理边界。
- 数组开小。
我退役罢