感觉这场难度不大啊,我讨厌手速场。
D
有一些细节的模拟题。
消除的总行数为所有列中方格数量的最小值,下面记为 \(sum\)。
设每个方格在本列中从下到上处在 \(k\) 的位置,那么:
-
\(k \le sum\),这个方格被消除的时间为所有处在 \(k\) 位置的格子中,\(x\) 的最大值;
-
\(k >sum\),这个方格不会被消除。
排序后直接做就行。
总体复杂度 \(O(n \log n+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,设 \(f_{x,0/1}\) 表示 \(x\) 这个节点最终为 \(0/1\) 的最小代价,初始全为 \(\inf\),转移为:
需要对叶子节点初始化:
需要对叶子节点的父亲节点特殊转移:
总体复杂度为 \(O(3^n)\)。
#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)=a_ib_j+b_jc_k+a_ic_k\),那么有:
所以直接优先队列维护同一层级,不确定单调性的 \(f\),每次取出最大值再拓展新的 \(f\) 即可。
总体复杂度 \(O(k \log k)\)
#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,内层是对 \(S\) 和 \(T\) 的 LCS 的 DP。
考虑 LCS 的 DP,设 \(f_{i,j}\) 表示 \(T\) 匹配到 \(i\) 位置,\(S\) 匹配到 \(j\) 位置的 LCS。
在外层 DP 时,记录内层 DP 的状态,也就是记录 \(f_i\) 就行了。
设 \(g_{i,s}\) 表示考虑到 \(T\) 的前 \(i\) 位,\(S\) 和 \(T\) 的 LCS DP 数组为 \(s\) 的方案数。
枚举当前位置填什么字符,在内层求出 \(s\) 的变化,直接转移即可。
因为 \(0\le f_{i,j}-f_{i,j-1} \le 1\),所以 \(s\) 的上界为 \(2^n\)。
在实现上,为了方便,可以在状态中记录 \(f_i\) 的差分数组。
总体复杂度为 \(O(m\sigma 2^m)\),\(\sigma\) 是字符集大小。
#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;
}