2019 Multi-University Training Contest 1
B
题意:
给定一个长度为n的序列,进行如下两种操作:
强制在线
题解:线性基+贪心
对于前缀 a[1……i],计算出线性基,然后用这组线性基来处理右端点为 i 的询问。( pla[i] 表示前缀 a[1……i] 的线性基)
贪心,如果构造过程中,使得线性基中的元素尽量靠后,那么在处理时即可求得最优解。
然而,如何保证线性基中的元素尽量靠后呢?当插入元素 i 时,遍历线性基的位置,可以插入,考虑如下两种情况:
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1e6+10;
int pla[N][31],pos[N][31];
void insert(int opt,int posi,int x)
{
for(int i=30;i>=0;i--)
if((x>>i)&1)
{
if(!pla[opt][i]){ pla[opt][i]=x; pos[opt][i]=posi; return;}
else
{
if(pos[opt][i]<posi) { swap(pla[opt][i],x); swap(pos[opt][i],posi); }
x^=pla[opt][i];
}
}
}
int query(int l,int r)
{
int ans=0;
for(int i=30;i>=0;i--)
if(pos[r][i]>=l&&(ans^pla[r][i])>ans)
ans^=pla[r][i];
return ans;
}
int main()
{
int T,n,m,x,l,r,opt,ans;
scanf("%d",&T);
while(T)
{
T--;
ans=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
for(int j=0;j<=30;j++)
{
pla[i][j]=pla[i-1][j];
pos[i][j]=pos[i-1][j];
}
insert(i,i,x);
}
for(int i=1;i<=m;i++)
{
scanf("%d",&opt);
if(opt==1)
{
scanf("%d",&x);
x^=ans;
n++;
for(int j=0;j<=30;j++)
{
pla[n][j]=pla[n-1][j];
pos[n][j]=pos[n-1][j];
}
insert(n,n,x);
}
else
{
scanf("%d%d",&l,&r);
l=(l^ans)%n+1; r=(r^ans)%n+1;
if(l>r) swap(l,r);
ans=query(l,r);
printf("%d\n",ans);
}
}
}
return 0;
}
E
题意:给一个有向图,删除某些边,使得最短路变短。
题解:最短路+网络流
跑最短路,将存在最短路上的边加入网络(可以证明是DAG图),跑最大流(求最小割)。
P.S. 注意 dfs 的时候要判 vist !!!
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const long long inf=1e16;
const int N=1e4+10;
struct apple{
int be,to,re;
long long u,v;
}edge[N*2];
struct NODE{
int x;
long long v;
bool operator <(const NODE &other)
const {return v>other.v;}
NODE(){}
NODE(int a,long long b){x=a;v=b;}
}now;
priority_queue<NODE>qque;
vector<int>ed[N],ed2[N];
vector<int>vd[N],vd2[N];
int t[N],vist[N],que[N],cur[N],d[N],vv[N];
long long dist[N];
int k,n,m;
long long ans;
void myclear()
{
for(int i=1;i<=n;i++)
{
t[i]=0; cur[i]=0; d[i]=0; vist[i]=0; vv[i]=0;
ed[i].clear(); vd[i].clear();ed2[i].clear();vd2[i].clear();
dist[i]=inf;
}
k=0; ans=0;
}
void addedge(int x,int y,long long v)
{
edge[++k].to=y; edge[k].be=t[x];
edge[k].v=v; edge[k].u=0;
edge[k].re=k+1;
t[x]=k;
edge[++k].to=x; edge[k].be=t[y];
edge[k].v=0; edge[k].u=0;
edge[k].re=k-1;
t[y]=k;
}
int bfs()
{
int head,tail,x,xx,p;
for(int i=1;i<=n;i++)
vist[i]=0;
que[1]=1; vist[1]=1;
head=0; tail=1;
while(head<tail)
{
head++; x=que[head];
p=t[x];
while(p)
{
xx=edge[p].to;
if(vist[xx]==0&&edge[p].v>edge[p].u)
{
d[xx]=d[x]+1; vist[xx]=1; que[++tail]=xx;
}
p=edge[p].be;
}
}
return vist[n];
}
long long dfs(int x,long long maxa)
{
if(x==n||maxa==0)
return maxa;
int &p=cur[x];
long long flow=0,f;
int xx;
while(p)
{
xx=edge[p].to;
if(edge[p].v>edge[p].u&&d[xx]==d[x]+1)
{
if(maxa<edge[p].v-edge[p].u)
f=dfs(xx,maxa);
else
f=dfs(xx,edge[p].v-edge[p].u);
if(f)
{
flow+=f;
maxa-=f;
edge[p].u+=f;
edge[edge[p].re].u-=f;
}
}
if(maxa==0)
break;
p=edge[p].be;
}
return flow;
}
void dij()
{
dist[1]=0;
qque.push(NODE(1,0));
while(!qque.empty())
{
now=qque.top(); qque.pop();
int x=now.x;
if(vist[x])
continue;
vist[x]=1; dist[x]=now.v;
int size=ed[x].size();
for(int i=0;i<size;i++)
{
int y=ed[x][i];
if(dist[y]>dist[x]+vd[x][i])
{
dist[y]=dist[x]+vd[x][i];
qque.push(NODE(y,dist[y]));
}
}
}
}
void dfs1(int x)
{
vv[x]=1;
int size=ed2[x].size();
for(int i=0;i<size;i++)
{
int y=ed2[x][i];
if(dist[x]==dist[y]+vd2[x][i])
{
addedge(y,x,vd2[x][i]);
if(!vv[y])
dfs1(y);
}
}
}
int main(){
int T,x,y,i;
long long c;
scanf("%d",&T);
while(T)
{
T--;
scanf("%d%d",&n,&m);
myclear();
for(i=1;i<=m;i++)
{
scanf("%d%d%lld",&x,&y,&c);
ed[x].push_back(y);
vd[x].push_back(c);
ed2[y].push_back(x);
vd2[y].push_back(c);
}
dij();
for(i=1;i<=n;i++)
vist[i]=0;
dfs1(n);
while(bfs())
{
for(i=1;i<=n;i++)
cur[i]=t[i];
ans+=dfs(1,inf);
}
printf("%lld\n",ans);
}
return 0;
}
F
题意:生成指定字符串,有两种生成操作:
题解:SAM+DP
首先,f[i] 是递增的。
对于 s[1……i] 的串,我们希望找到一个最小的 j 使得 s[j+1……i] 在 s[1……j] 中出现过。可以在后缀自动机上匹配,但是,由于可以匹配到 j 的位置可能很多,所以复杂度困难达到O(n2)
考虑一个性质,对于 i 位置,如果它可以匹配到的最小位置为 j ,那么 i+1 位置可以匹配到的最小位置也不早于 j 。因此,在每一次新增一个字符时,判断当前 s[1……j] 是否满足添加,如果不满足,则将 j+1 加入后缀自动机中,如果满足则更新 f[i] 。
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=2e6+10;
int nxt[N][26],pre[N],len[N];
long long f[N];
char s[N];
int last,cnt;
void insert(int x)
{
int p,q,np,nq;
np=++cnt;
p=last;
len[np]=len[p]+1;
while(p&&!nxt[p][x])
{
nxt[p][x]=np;
p=pre[p];
}
if(!p)
pre[np]=1;
else
{
q=nxt[p][x];
if(len[p]+1==len[q])
pre[np]=q;
else
{
nq=++cnt;
len[nq]=len[p]+1;
for(int i=0;i<26;i++)
nxt[nq][i]=nxt[q][i];
pre[nq]=pre[q];
while(p&&nxt[p][x]==q)
{
nxt[p][x]=nq;
p=pre[p];
}
pre[np]=pre[q]=nq;
}
}
last=np;
}
int main()
{
long long a,b;
int i,j,now,n;
while(scanf("%s",s+1)!=EOF)
{
n=strlen(s+1);
scanf("%lld%lld",&a,&b);
f[1]=a;
last=cnt=1;
len[1]=pre[1]=0;
insert(s[1]-'a');
j=1;
now=nxt[1][s[1]-'a'];
for(i=2;i<=n;i++)
{
f[i]=f[i-1]+a;
while(true)
{
while(now>1&&len[pre[now]]>=(i-j-1))
now=pre[now];
if(nxt[now][s[i]-'a'])
{
now=nxt[now][s[i]-'a'];
break;
}
else
{
j++;
insert(s[j]-'a');
}
}
f[i]=min(f[i],f[j]+b);
}
for(i=1;i<=cnt;i++)
for(j=0;j<26;j++)
nxt[i][j]=0;
printf("%lld\n",f[n]);
}
return 0;
}