CF Round 999 题解合集
感觉出的都很不错,做起来很舒服。
C
考虑直接 DP。
设 表示考虑前 个人,第 个人是否说谎的方案数。
枚举第 个人是否说谎,得到转移:
注意边界条件。
复杂度 。
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int t,n,a[200005],f[200005][2];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
if(a[1]) f[1][0]=1;
else f[1][0]=f[1][1]=1;
for(int i=2;i<=n;i++){
if(a[i]-1==a[i-2]) f[i][1]=(f[i][1]+f[i-1][0])%mod;
if(a[i]==a[i-1]) f[i][1]=(f[i][1]+f[i-1][1])%mod;
f[i][0]=f[i-1][1];
}
cout<<(f[n][0]+f[n][1])%mod<<'\n';
for(int i=1;i<=n;i++){
f[i][0]=f[i][1]=0;
}
}
return 0;
}
D
考虑从大到小考虑目标序列中的每一个数,贪心地进行操作。
如果当前数 在初始序列中出现过,那么直接删去就行。否则,把 拆成 ,继续向下考虑。
如果目标序列中的数比初始序列多,或者当前的 不能再拆了,就无解。否则一定有解。
实现上考虑使用 mutiset,复杂度 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,m,a[200005],b[200005],ansa,ansb,flag;
multiset<int> sa,sb;
void init(){
ansa=ansb=flag=0;
sa.clear();
sb.clear();
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
init();
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
ansa+=a[i];
sa.insert(a[i]);
}
for(int i=1;i<=m;i++){
cin>>b[i];
ansb+=b[i];
sb.insert(b[i]);
}
if(ansa!=ansb){
cout<<"No"<<'\n';
continue;
}
for(int i=1;i<=n;i++){
sa.insert(a[i]);
}
for(int i=1;i<=m;i++){
sb.insert(b[i]);
}
while(sb.size()){
multiset<int>::iterator it=prev(sb.end());
if(sa.find(*it)!=sa.end()){
sa.erase(sa.find(*it));
}else{
if((*it)==1){
flag=true;
break;
}else{
sb.insert((*it)/2);
sb.insert((*it)-(*it)/2);
}
}
sb.erase(it);
if(sb.size()>sa.size()){
flag=true;
break;
}
}
if(flag) cout<<"No"<<'\n';
else cout<<"Yes"<<'\n';
}
return 0;
}
E
考虑操作顺序是假的,我们只关心对每个数进行了几次操作。
因为 很小,我们可以对于每一个 暴力算出操作 次的最小值,记为 。
然后就是经典的多路归并贪心了,用堆维护。
复杂度 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int all=(1<<30)-1,inf=1e12;
int t,n,m,k,a[100005],b[100005],c[1025],f[100005][11],ans;
struct node{
int id,cnt,val;
bool operator <(const node &a)const{
return a.val<val;
}
};
priority_queue<node> q;
void init(){
ans=0;
for(register int i=1;i<=n;i++){
for(register int j=1;j<=m;j++){
f[i][j]=inf;
}
}
while(q.size()) q.pop();
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>n>>m>>k;
init();
for(int i=1;i<=n;i++){
cin>>a[i];
ans+=a[i];
}
for(int i=1;i<=m;i++){
cin>>b[i];
}
for(int s=1;s<(1<<m);s++){
c[s]=all;
for(int i=1;i<=m;i++){
if(s&(1<<(i-1))) c[s]&=b[i];
}
}
for(register int i=1;i<=n;i++){
for(register int s=1;s<(1<<m);s++){
int l=__builtin_popcount(s);
f[i][l]=min(f[i][l],a[i]&c[s]);
}
}
for(int i=1;i<=n;i++){
q.push((node){i,1,f[i][1]-a[i]});
}
while(k--){
node tmp=q.top();
q.pop();
ans+=tmp.val;
if(tmp.cnt<m){
tmp.cnt++;
tmp.val=f[tmp.id][tmp.cnt]-f[tmp.id][tmp.cnt-1];
q.push(tmp);
}
}
cout<<ans<<'\n';
}
return 0;
}
F1
感觉又遇见了一次 NOIP 2024 T1。
考虑最小化操作次数是假的,因为合法操作序列是唯一的。
考虑从 枚举 ,假设枚举到 的时候 和 已经对应好了。
-
如果 ,那么不用管这一位。
-
否则:
-
- ,一定死,因为后面的数根本换不过来。
-
- 否则,设 表示 这一块, 表示 中有 个 :
-
-
- 如果 或者 ,一定死,因为后面的数还是换不过来。
-
-
-
- 否则,更新交换次数为 中 块的数量,更新 的值。
-
然后就做到均摊 了。
#include<bits/stdc++.h>
using namespace std;
string s1,s2;
int t,n,a[400005],b[400005],ans;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
cin>>s1;
cin>>s2;
n=s1.size();
for(int i=1;i<=n;i++){
a[i]=s1[i-1]-'0';
}
for(int i=1;i<=n;i++){
b[i]=s2[i-1]-'0';
}
for(int i=1;i<=n;i++){
if(a[i]!=b[i]){
if(i>1 && a[i-1]==a[i]){
ans=-1;
break;
}
int j=i;
while(j+1<=n && b[j+1]==b[i]) j++;
int k=i,cnt=0;
while(k<=n && cnt<(j-i+1)) k++,cnt+=(a[k]==b[i]);
if(k>n || (k<n && a[k]==a[k+1])){
ans=-1;
break;
}
for(int l=i+1;l<=k;l++){
if(a[l]==b[i] && a[l]!=a[l-1]) ans++;
}
for(int l=i;l<=j;l++){
a[l]=b[i];
}
for(int l=j+1;l<=k;l++){
a[l]=(!b[i]);
}
i=j;
}
}
cout<<ans<<'\n';
ans=0;
}
return 0;
}
G
遇到自适应交互库先考虑它是怎么写的。
不难发现,想要使答案达到下界,只需要把点均分成 份,选 份连一个团,剩下一份当成孤立点。
此时的答案为 。
我们断言,这个答案是一定合法的,考虑这样一种构造方法:
用一个栈维护当前的同色链,每次加入一个点。
-
询问这个点和栈顶的边的颜色,如果颜色和链的颜色相同,那么把这个点加入栈,否则,把栈顶和次栈顶弹出,把这两条异色边都存下来。
-
枚举完所有点之后,把当前栈清空,并记录栈内所有边。
分析一下,在最劣情况下,我们用 个点, 次询问,确定了 条颜色边,因此,我们这么询问是可以满足 个点对的。
#include<bits/stdc++.h>
using namespace std;
int t,n,k,c1,c2,x,y;
stack<int> d;
struct node{
int x,y;
};
vector<node> ans[2];
int main(){
cin>>t;
while(t--){
cin>>n;
k=(n+1)/3;
cout<<k<<endl;
for(int i=1;i<=n;i++){
if(!d.size()) d.push(i);
else{
x=d.top();
cout<<"? "<<x<<' '<<i<<endl;
cin>>c2;
if(d.size()==1) c1=c2;
if(c1!=c2){
d.pop();
y=d.top();
d.pop();
ans[c1].push_back((node){x,y});
ans[c2].push_back((node){x,i});
}else d.push(i);
}
}
while(d.size()>=2){
x=d.top();
d.pop();
y=d.top();
d.pop();
ans[c1].push_back((node){x,y});
}
if(ans[0].size()<ans[1].size()) swap(ans[0],ans[1]);
cout<<"! ";
for(int i=0;i<min(k,(int)ans[0].size());i++){
cout<<ans[0][i].x<<' '<<ans[0][i].y<<' ';
}
cout<<endl;
ans[0].clear();
ans[1].clear();
while(d.size()) d.pop();
}
return 0;
}
本文作者:Kenma
本文链接:https://www.cnblogs.com/Kenma/p/18722435
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步