2019 Multi-University Training Contest 1

2019 Multi-University Training Contest 1
B
题意:
给定一个长度为n的序列,进行如下两种操作:

  1. 在末尾添加一个数
  2. 询问某区间的最大异或和

强制在线
题解:线性基+贪心
对于前缀 a[1……i],计算出线性基,然后用这组线性基来处理右端点为 i 的询问。( pla[i] 表示前缀 a[1……i] 的线性基)
贪心,如果构造过程中,使得线性基中的元素尽量靠后,那么在处理时即可求得最优解。
然而,如何保证线性基中的元素尽量靠后呢?当插入元素 i 时,遍历线性基的位置,可以插入,考虑如下两种情况:

  1. 当前基位置为空,直接插入
  2. 当前基位置不为空且处于当前基位置的元素比要插入的元素考前,用要插入的元素换出当前基,并用当前基进行下一步试探。
    重复以上两步骤。
#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
题意:生成指定字符串,有两种生成操作:

  1. 在末尾添加任意字符,花费 a
  2. 复制一段已经生成的字符串并添加到末尾,花费 b

题解: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;
}

你可能感兴趣的:(训练赛)