2023.8.25

A

两人轮流从字符串首尾取字符,问按最优策略谁获胜。

保证字符串长度为偶数。多测。

\(\sum |S|\le 2000\).

考虑对于每个区间 \(f_{i,j}\) 直接做。对于 \(s_i\)\(s_j\) 是否相等分讨,容易想出来平凡的转移。

就是我写的比较屎山。

#include<bits/stdc++.h>
#define N 2010
#define inf 2e9
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int T,n;char s[N];
int f[N][N];//0A1D2B
int main(){
	T=read();
	while(T--){
		scanf("%s",s+1),n=strlen(s+1);
		for(int i=0;i<=n;i++)f[i+1][i]=1;
		for(int len=2;len<=n;len+=2)
			for(int i=1,j=i+len-1;j<=n;i++,j++){
				if(len==2){
					f[i][j]=(s[i]==s[j])?1:0;
					continue;
				}
				f[i][j]=0;
				if(s[i]!=s[j]){
					if(s[i]<s[j]){
						if(s[i+1]<s[i])f[i][j]=2;
						if(s[i+1]==s[i])f[i][j]=f[i+2][j];
						if(s[i+1]>s[i])f[i][j]=0;
					}
					else{
						if(s[j-1]<s[j])f[i][j]=2;
						if(s[j-1]==s[j])f[i][j]=f[i][j-2];
						if(s[j-1]>s[j])f[i][j]=0;
					}
				}
				else{
					if(s[i+1]<s[i]&&s[j-1]<s[j])f[i][j]=2;
					else if(s[i+1]<s[i]&&s[j-1]>s[j])f[i][j]=0;
					else if(s[i+1]>s[i]&&s[j-1]<s[j])f[i][j]=0;
					else if(s[i+1]>s[i]&&s[j-1]>s[j])f[i][j]=f[i+1][j-1];
					f[i][j]=max(f[i][j],f[i+1][j-1]);
					if(s[i+1]==s[i])f[i][j]=max(f[i][j],f[i+2][j]);
					if(s[j-1]==s[j])f[i][j]=max(f[i][j],f[i][j-2]);
				}
			}
		puts(!f[1][n]?"Alice":(f[1][n]==1?"Draw":"Bob"));
	}
	
	return 0;
}

B

一张带权无向图,有两个内鬼初始在 \(x\)\(y\),有 \(k\) 个目标。

此外有 \(e\) 条情报,每条是一个三元组 \((p,x,t)\) 表示目标 \(p\)\(t\) 时刻出现在点 \(x\).

若时间点 \(t\) 有任何一个内鬼在点 \(x\) 那么目标 \(p\) 被捕获。

问内鬼捕获全部目标的最短时间,或报告无法捕获所有目标。多测。

\(\sum n\le 10^4\)\(\sum m\le 2\times 10^4\)\(\sum e\le 10^5\)\(1\le k\le 8\)\(1\le t\le 10^8\).

容易想到 \(f_{S,v}\) 表示抓了 \(S\) 里面的人,当前在 \(v\) 点的最少时间。

对于 “移动” 和 “蹲点” 分别转移,前者即固定 \(S\),把所有 \(v\) 的最短路更新。

看完 std 脑洞大开。

#include<bits/stdc++.h>
#define N 10005
#define M 20010
#define K 1<<8
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define vit vector<int>::iterator
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
const int inf=0x3f3f3f3f;
int T,n,m,k,E,lim;
int head[N],nxt[M<<1],ver[M<<1],dist[M<<1],tot;
void add(int u,int v,int w){
	nxt[++tot]=head[u];
	ver[tot]=v,dist[tot]=w;
	head[u]=tot;
}
vector<int>vec[9][N];
int f[K][N],g[K];
void dijkstra(int *f){
	priority_queue<pii>q;
	for(int i=1;i<=n;i++)q.push(mp(-f[i],i));
	while(!q.empty()){
		int u=q.top().se,w=q.top().fi;q.pop();
		if(f[u]!=-w)continue;
		for(int i=head[u],v;i;i=nxt[i]){
			if(f[v=ver[i]]>f[u]+dist[i]){
				f[v]=f[u]+dist[i];
				q.push(mp(-f[v],v));
			}
		}
	}
}
void solve(int x){
	memset(f,0x3f,sizeof(f)),f[0][x]=0;
	for(int S=0;S<lim;S++){
		dijkstra(f[S]);
		for(int i=1;i<=n;i++){
			int tim=min(inf,f[S][i]);
			for(int j=1;j<=k;j++)
				if(!(S&(1<<j-1))){
					int tp=*lower_bound(vec[j][i].begin(),vec[j][i].end(),tim);
					f[S^(1<<j-1)][i]=min(f[S^(1<<j-1)][i],tp);
				}
		}
	}
}
int main(){
	T=read();
	while(T--){
		n=read(),m=read(),k=read(),lim=(1<<k);
		lim=(1<<k),tot=0;
		for(int i=1;i<=n;i++){
			head[i]=0;
			for(int j=1;j<=k;j++)
				vec[j][i].clear();
		}
		for(int i=1,u,v,w;i<=m;i++){
			u=read(),v=read(),w=read();
			add(u,v,w),add(v,u,w);
		}
		E=read();
		for(int i=1,p,x,t;i<=E;i++){
			p=read(),x=read(),t=read();
			vec[p][x].push_back(t);
		}
		for(int i=1;i<=k;i++)
			for(int j=1;j<=n;j++){
				vec[i][j].push_back(inf);
				sort(vec[i][j].begin(),vec[i][j].end());
			}
		int x=read();solve(x);
		for(int S=0;S<lim;S++){
			g[S]=inf;
			for(int i=1;i<=n;i++)
				g[S]=min(g[S],f[S][i]);
		}
		int y=read();solve(y);
		int ans=inf;
		for(int S=0;S<lim;S++){
			int mn=inf;
			for(int i=1;i<=n;i++)
				mn=min(mn,f[S][i]);
			ans=min(ans,max(mn,g[(lim-1)^S]));
		}
		if(ans>=inf)printf("-1\n");
		else printf("%d\n",ans);
	}
	
	return 0;
}

C

2020ICPC·小米 网络选拔赛第一场 E.Phone Network

这也能开出来?

给一个值域 \([1,m]\) 的数列 \(\{a_n\}\),保证每个数至少出现一次。

对于 \(i\in[1,m]\),求出一个最短的区间长度,使得其包含 \([1,i]\) 的所有数。

\(n,m\le 5\times 10^5\).

考虑维护 \(R_{i,l}\) 表示以 \(l\) 为左端点,包含 \([1,i]\) 的最小右端点,考虑从 \(i\) 转移到 \(i+1\).

记数字 \(i+1\) 的位置为 \(p_1,p_2,\dots,p_k\),那么维护 \(R\) 数组就变成了区间 \(\max\).

注意到对于一个 \(i\)\(R_{i,1\sim n}\) 单调,故可以在 \([p_{j-i}+1,p_j]\) 中找到 \(R_{i+1,l}<p_j\) 的一段 \(l\),就变成区间赋值了。

需要维护 \(R_{i,l}-l+1\) 的值,每次取最小值作为答案。

怎么维护 \(\min R_{i,l}-l+1\) 呢?

在线段树的节点上记录这个值,那么区间赋值的使用可以直接用懒标记更新,即:

\[\min_{k=l}^{r}R_{i,k}-k+1=lazy-r+1 \]

#include<bits/stdc++.h>
#define N 500010
#define inf 0x3f3f3f3f
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
struct Tree{
	int l,r,mn,res,tag;
	#define l(x) t[x].l
	#define r(x) t[x].r
	#define mn(x) t[x].mn
	#define res(x) t[x].res
	#define tag(x) t[x].tag
}t[N<<2];
#define ls p<<1
#define rs p<<1|1
void build(int p,int l,int r){
	l(p)=l,r(p)=r,res(p)=inf;
	if(l==r)return;
	int mid=(l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
}
void pushup(int p){
	mn(p)=min(mn(ls),mn(rs));
	res(p)=min(res(ls),res(rs));
}
void pushdown(int p){
	if(!tag(p))return;
	tag(ls)=tag(rs)=mn(ls)=mn(rs)=tag(p);
	res(ls)=tag(p)-r(ls)+1,res(rs)=tag(p)-r(rs)+1;
	tag(p)=0;
}
void modify(int p,int l,int r,int x){
	if(l<=l(p)&&r(p)<=r){
		mn(p)=tag(p)=x,res(p)=x-r(p)+1;
		return;
	}
	pushdown(p);
	int mid=(l(p)+r(p))>>1;
	if(l<=mid)modify(ls,l,r,x);
	if(r>mid)modify(rs,l,r,x);
	pushup(p);
}
int find(int p,int l,int r){
	if(mn(p)>=r||r(p)<l||l(p)>r)return -1;
	if(l(p)==r(p))return l(p);
	int ans=find(rs,l,r);
	return ans==-1?find(ls,l,r):ans;
}
int n,m;
vector<int>pos[N];
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)
		pos[read()].push_back(i);
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int pre=0;
		for(int now:pos[i]){
			int p=find(1,pre+1,now);
			if(p!=-1)modify(1,pre+1,p,now);
			pre=now;
		}
		if(pos[i].back()<n)
			modify(1,pos[i].back()+1,n,inf);
		printf("%d\n",res(1));
	}
	
	return 0;
}

D

没活了,给大伙整一个经典最短路吧。

严格弱化版:The Classic Problem

弱化版是 *3000 的。

给一个有向图,共 \(m\) 组边,每组边描述一个六元组 \((l_1,r_1,l_2,r_2,a,b)\),表示所有 \(x\in[l_1,r_1]\)\(y\in[l_2,r_2]\) 连一条长为 \(a\cdot 2^b\) 的有向边。

\(1\) 到各点的最短路。答案对 \(\rm 1e9+7\) 取模。

需要在严格弱化版的基础上加入拆点和线段树优化。

posted @ 2023-08-25 16:28  SError  阅读(31)  评论(0编辑  收藏  举报