人,只有自己站起来,这个世界才能属于他。|

园龄:粉丝:关注:

abc391 题解合集

here.

感觉这场难度不大啊,我讨厌手速场。

D

有一些细节的模拟题。

消除的总行数为所有列中方格数量的最小值,下面记为 sum

设每个方格在本列中从下到上处在 k 的位置,那么:

  • ksum,这个方格被消除的时间为所有处在 k 位置的格子中,x 的最大值;

  • k>sum,这个方格不会被消除。

排序后直接做就行。

总体复杂度 O(nlogn+q)

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9+10; 
int n,m,q,x,y,ans,flag,maxn,minn,tim[1000005];
struct node{
	int x,id;
};
vector<node> v[1000005];
bool cmp(node a,node b){
	return a.x<b.x;
}
int main(){
	flag=true;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>x>>y;
		tim[i]=inf;
		v[x].push_back((node){y,i}); 
	}
	ans=inf;
	for(int i=1;i<=m;i++){
		sort(v[i].begin(),v[i].end(),cmp);
		ans=min(ans,(int)v[i].size());
	}
	for(int i=0;i<ans;i++){
		maxn=0;
		for(int j=1;j<=m;j++){
			maxn=max(maxn,v[j][i].x);
		}
		for(int j=1;j<=m;j++){
			tim[v[j][i].id]=maxn;
	    }
	}
	cin>>q;
	for(int i=1;i<=q;i++){
		cin>>y>>x;
		if(y<tim[x]) cout<<"Yes"<<'\n';
		else cout<<"No"<<'\n';
	}
	return 0;
} 

E

看起来就像一个树形结构,考虑直接在序列上建三叉树,每个叶子节点表示序列上的一个位置。

考虑 DP,设 fx,0/1 表示 x 这个节点最终为 0/1 的最小代价,初始全为 inf,转移为:

fx,0=miny1sonx,y2sonxfy1,0+fy2,0

fx,1=miny1sonx,y2sonxfy1,1+fy2,1

需要对叶子节点初始化:

fx,ai=0

需要对叶子节点的父亲节点特殊转移:

fx,0=miny1sonx,y2sonxmin(fy1,0,1)+min(fy2,0,1)

fx,1=miny1sonx,y2sonxmin(fy1,1,1)+min(fy2,1,1)

总体复杂度为 O(3n)

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
char ch;
int n,m,dcnt,id[3000005],a[3000005],f[3000005][2];
vector<int> v[3000005];
bool cmp0(int x,int y){
	return f[x][0]<f[y][0];
}
bool cmp1(int x,int y){
	return f[x][1]<f[y][1];
}
void build(int l,int r,int fa){
	int x=++dcnt;
	v[fa].push_back(x);
	if(l==r){
		id[x]=l;
		return;
	}
	int mid1=(r-l+1)/3+l-1,mid2=(r-l+1)/3*2+l-1;
	build(l,mid1,x);
	build(mid1+1,mid2,x);
	build(mid2+1,r,x);
}
void dfs(int x){
	if(!v[x].size()){
		f[x][a[id[x]]]=0;
		return;
	} 
	for(int i=0;i<v[x].size();i++){
		dfs(v[x][i]);
	}
	sort(v[x].begin(),v[x].end(),cmp0);
	if(id[v[x][0]]) f[x][0]=min(f[v[x][0]][0],1)+min(f[v[x][1]][0],1);
	else f[x][0]=f[v[x][0]][0]+f[v[x][1]][0];
	sort(v[x].begin(),v[x].end(),cmp1);
	if(id[v[x][0]]) f[x][1]=min(f[v[x][0]][1],1)+min(f[v[x][1]][1],1);
	else f[x][1]=f[v[x][0]][1]+f[v[x][1]][1];
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	m=pow(3,n);
	for(int i=1;i<=m;i++){
		cin>>ch;
		a[i]=ch-'0';
	}
	build(1,m,0);
	for(int i=1;i<=dcnt;i++){
		f[i][0]=f[i][1]=inf;
	}
	dfs(1);
	if(!f[1][0]) cout<<f[1][1];
	else cout<<f[1][0];
	return 0; 
}

F

典。

考虑将 a,b,c 升序排序后,设 f(i,j,k)=aibj+bjck+aick,那么有:

f(i,j,k)f(i+1,j,k),f(i,j+1,k),f(i,j,k+1)f(i+1,j+1,k),f(i,j+1,k+1),f(i+1,j,k+1)f(i+1,j+1,k+1)

所以直接优先队列维护同一层级,不确定单调性的 f,每次取出最大值再拓展新的 f 即可。

总体复杂度 O(klogk)

#include<bits/stdc++.h>
#define int __int128
using namespace std;
template<typename T> inline void read(T &a){
	a=0;
	char c=getchar();
	bool flag=false;
	while(!isdigit(c)){
		if(c=='-') flag=true;
		c=getchar();
	}
	while(isdigit(c)){
		a=(a<<1)+(a<<3)+(c^48);
		c=getchar(); 
	}
	a=(flag?-a:a);
}
template<typename T> inline void write(T a){
    if(a<0) putchar('-'),a=-a;
    if(a<10) putchar(a+'0');
    else write(a/10),putchar(a%10+'0');
}
int n,k,cnt,a[200005],b[200005],c[200005];
int val(int i,int j,int k){
	return a[i]*b[j]+b[j]*c[k]+a[i]*c[k];
}
bool cmp(int x,int y){
	return x>y;
}
struct node{
	int a,b,c,val;
	bool operator <(const node &d)const{
		if(val!=d.val) return val<d.val;
		if(a!=d.a) return a<d.a;
		if(b!=d.b) return b<d.b;
		return c<d.c;
	}
	bool operator ==(const node &d)const{
		return a==d.a && b==d.b && c==d.c;
	}
};
priority_queue<node> q;
map<node,bool> vis; 
signed main(){
	read(n),read(k); 
	for(int i=1;i<=n;i++){
		read(a[i]);
	}
	for(int i=1;i<=n;i++){
		read(b[i]);
	}
	for(int i=1;i<=n;i++){
		read(c[i]);
	}
	sort(a+1,a+1+n,cmp);
	sort(b+1,b+1+n,cmp);
	sort(c+1,c+1+n,cmp);
	q.push((node){1,1,1,val(1,1,1)});
	while(cnt<k){
		node t=q.top();
		q.pop();
		if(vis[t]) continue;
		vis[t]=1;
		cnt++;
		if(cnt==k){
			write(t.val);
			break;
		}
		if(t.a<n) q.push((node){t.a+1,t.b,t.c,val(t.a+1,t.b,t.c)});
		if(t.b<n) q.push((node){t.a,t.b+1,t.c,val(t.a,t.b+1,t.c)});
		if(t.c<n) q.push((node){t.a,t.b,t.c+1,val(t.a,t.b,t.c+1)});
	}
	return 0;
	 
}

G

游园会.jpg

但是我做这个题的时候已经忘了游园会怎么做了。

不难发现是 DP of DP,外层是对 T 形态的 DP,内层是对 ST 的 LCS 的 DP。

考虑 LCS 的 DP,设 fi,j 表示 T 匹配到 i 位置,S 匹配到 j 位置的 LCS。

在外层 DP 时,记录内层 DP 的状态,也就是记录 fi 就行了。

gi,s 表示考虑到 T 的前 i 位,ST 的 LCS DP 数组为 s 的方案数。

枚举当前位置填什么字符,在内层求出 s 的变化,直接转移即可。

因为 0fi,jfi,j11,所以 s 的上界为 2n

在实现上,为了方便,可以在状态中记录 fi 的差分数组。

总体复杂度为 O(mσ2m)σ 是字符集大小。

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
char a[15];
int g[15],h[15],n,m,ans;
int get(int ch,int s){
	int t=0;
	for(int i=1;i<=n;i++){
		g[i]=g[i-1]+(bool)(s&(1<<(i-1)));
    }
	for(int i=1;i<=n;i++){
		if(a[i]-'a'==ch) h[i]=g[i-1]+1;
		else h[i]=max(h[i-1],g[i]);
	}
	for(int i=1;i<=n;i++){
		t|=(1<<(i-1))*(h[i]-h[i-1]);
	}
	return t;
}
int f[101][1025];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	f[0][0]=1;
	for(int i=1;i<=m;i++){
		for(int s=0;s<(1<<n);s++){
			for(int j=0;j<26;j++){
				int t=get(j,s);
				f[i][t]=(f[i][t]+f[i-1][s])%mod;
			}
	    }
	}
	for(int i=0;i<=n;i++){
		ans=0;
		for(int s=0;s<(1<<n);s++){
			if(__builtin_popcount(s)==i) ans=(ans+f[m][s])%mod;
		}
		cout<<ans<<' ';
	}
	return 0;
}

本文作者:Kenma

本文链接:https://www.cnblogs.com/Kenma/p/18696757

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   _Kenma  阅读(86)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起