abc391 题解合集
感觉这场难度不大啊,我讨厌手速场。
D
有一些细节的模拟题。
消除的总行数为所有列中方格数量的最小值,下面记为 。
设每个方格在本列中从下到上处在 的位置,那么:
-
,这个方格被消除的时间为所有处在 位置的格子中, 的最大值;
-
,这个方格不会被消除。
排序后直接做就行。
总体复杂度 。
#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,设 表示 这个节点最终为 的最小代价,初始全为 ,转移为:
需要对叶子节点初始化:
需要对叶子节点的父亲节点特殊转移:
总体复杂度为 。
#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
典。
考虑将 升序排序后,设 ,那么有:
所以直接优先队列维护同一层级,不确定单调性的 ,每次取出最大值再拓展新的 即可。
总体复杂度 。
#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,外层是对 形态的 DP,内层是对 和 的 LCS 的 DP。
考虑 LCS 的 DP,设 表示 匹配到 位置, 匹配到 位置的 LCS。
在外层 DP 时,记录内层 DP 的状态,也就是记录 就行了。
设 表示考虑到 的前 位, 和 的 LCS DP 数组为 的方案数。
枚举当前位置填什么字符,在内层求出 的变化,直接转移即可。
因为 ,所以 的上界为 。
在实现上,为了方便,可以在状态中记录 的差分数组。
总体复杂度为 , 是字符集大小。
#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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步