Codeforces Round 889 Div.2 A-F
前言:wssb
Dalton the Teacher
题意:给定一个排列,每次可以交换两个元素,求使得
一次可以操作两个元素,故答案为
Longest Divisors Interval
题意:给定若干个
wssb,这个题猜了结论不敢写,推NM个组合数在乱整
引理:在最优方案中,必定存在一种,使得
证明:
根据模运算的周期性,在任意区间
扩展一下,变为在任意区间
那么,接着变为
由此,若存在
故有
而又有:
由此,我们从1开始枚举,直到不是
Dual
直接讲C2。
题意:有
C1,C2打了两个完全不同的做法,谁知道两个做法粘在一起就过了。。
对于构造题,可以拆分问题:一个简化的形式+将一般问题化为简化的形式。我们一般先考虑前者再考虑后者。这个思想可以在后面的 F 见到
首先来考虑只有正数/负数的情况,显然我们可以做前/后缀和以最多
然后考虑将正常情况化为正数/负数。有两个办法:
- 找到最大/最小值,将负/正数加上它使得其变为负数
- 将一个值自增最多5次再将正负数化掉
注意到操作1的次数是
而操作2的次数是
则当
这是最关键的部分:当
所以根据
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
#define N 505050
int n,m,a[505];
struct node{
int x,y;
}ans[505];
int tot=0;
signed main(){
int t;cin>>t;
while(t--){
cin>>n;int c1=0,c2=0;tot=0;for(int i=1;i<=n;i++)cin>>a[i];
int mn=0x3f3f3f3f,mx=-0x3f3f3f3f;
for(int i=1;i<=n;i++)mx=max(mx,a[i]),mn=min(mn,a[i]);
if(mn>=0){
for(int i=2;i<=n;i++)ans[++tot]={i,i-1};
}
else if(mx<=0){
for(int i=n-1;i;--i)ans[++tot]=(node){i,i+1};
}
else {
for(int i=1;i<=n;i++)c1+=(a[i]<0),c2+=(a[i]>0);
if(12<c1){
int id=-1;
for(int i=1;i<=n;++i)if(a[i]==mn){
id=i;break;
}
while(mx+mn>0){
ans[++tot]={id,id};mn+=mn;
}
a[id]=mn;
for(int i=1;i<n;i++)if(a[i]>0)ans[++tot]={i,id},a[i]+=mn;
for(int i=n-1;i;i--)if(a[i]>a[i+1])a[i]+=a[i+1],ans[++tot]={i,i+1};
cout<<tot<<" \n";
for(int i=1;i<=tot;i++)cout<<ans[i].x<<" "<<ans[i].y<<"\n";
continue;
}
else if(12<c2){
int id=-1;
for(int i=n;i;--i)if(a[i]==mx){
id=i;break;
}
while(mx+mn<0){
ans[++tot]={id,id};mx+=mx;
}
a[id]=mx;
for(int i=2;i<=n;i++)if(a[i]<0)ans[++tot]={i,id},a[i]+=mx;
for(int i=2;i<=n;i++)if(a[i]<a[i-1])a[i]+=a[i-1],ans[++tot]={i,i-1};
}
else {
int mn=0x3f3f3f3f,mx=-0x3f3f3f3f;
for(int i=1;i<=n;i++)mx=max(mx,a[i]),mn=min(mn,a[i]);
if(mx+mn>0){
int id=-1;
for(int i=1;i<=n;i++){
if(a[i]==mx){
id=i;break;
}
}
for(int i=1;i<=n;i++)if(a[i]<0){
ans[++tot]=(node){i,id};
}
for(int i=2;i<=n;i++)ans[++tot]={i,i-1};
}
else{
int id=-1;
for(int i=1;i<=n;i++){
if(a[i]==mn){
id=i;break;
}
}
for(int i=1;i<=n;i++)if(a[i]>0){
ans[++tot]=(node){i,id};
}
for(int i=n-1;i;--i)ans[++tot]=(node){i,i+1};
}
}
}
cout<<tot<<" \n";
for(int i=1;i<=tot;i++)cout<<ans[i].x<<" "<<ans[i].y<<"\n";
}
}
Earn or Unlock
题意:最初有一堆牌,自顶向下编号
- 按顺序解锁
张牌(已解锁的牌跳过,总解锁 张)。 - 获得收益
。
求最后所得最大收益。
题解:
已经解锁的牌肯定都要用完。
考虑一种均摊的思想,其实选择操作一,本质上是选择将解锁的和自己总共
若解锁了
这本质上是求
设
用二进制数
但考虑是不是真的可以这样转移呢?注意到能用
所以我们要求转移时
注意到可能我们会将所有牌拿完而还有剩余次数,所以多扩展一倍,维护到
#include<bitset>
#include<iostream>
#define N 205050
#define int long long
using namespace std;
bitset<N>f;
int a[N],n,s[N],ans[N];
signed main(){
ios::sync_with_stdio(false);
cin>>n;for(int i=1;i<=n;i++)cin>>a[i];
s[0]=1;
for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i]-1;
f[1]=1;
for(int i=1;i<=n;i++){
f|=(f<<a[i]);
ans[i]=f[i];f[i]=0;
}
for(int i=n+1;i<=n<<1;i++){
ans[i]=f[i];s[i]=s[i-1]-1;
}
int res=0;
for(int i=1;i<=n<<1;i++){
if(ans[i])res=max(res,s[i]);
}
cout<<res<<"\n";
}
Expected Destruction
题意:有
题解;注意在本题中,一个数没被删之前,排名比它小(自大到小)的那个数也不会被删,所以本质上问题是独立的。我们需要将
设
设
注意当
用记忆化搜索即可。
#define int long long
int f[N][N],n,m,a[N],ans;
const int p=1e9+7;
const int inv=p+1>>1;
int dfs(int x,int y){
if(x==y)return 0;
if(f[x][y]!=-1)return f[x][y];
if(y==m+1)return m+1-x;
return f[x][y]=(1+dfs(x+1,y)+dfs(x,y+1))*inv%p;
}
signed main(){
cin>>n>>m;
memset(f,-1,sizeof f);
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=m;i++)for(int j=i+1;j<=m;j++)f[i][j]=dfs(i,j);
ans+=m+1-a[n];
for(int i=2;i<=n;i++)ans=(ans+f[a[i-1]][a[i]])%p;
cout<<ans<<"\n";
}
Michael and Hotel
有趣的问题。
题意:交互题,给一张不知道边指向的图,有有向边
这是一颗内向基环树,如果可以知道1所在连通块的环,则在
首先,我们可以在9次操作内找到点
其次,我们找到点
(如果这一步找到重复点,说明环已经找到,直接跳到最后一步)
然后,我们不断倍增这个步长,最初为
我们不断倍增,直到
操作次数为:
本质来讲,这是通过用
#include<bits/stdc++.h>
using namespace std;
int n,vis[5050];
vector<int>g,f;
int get(int l,int r,int k,int id){
if(l==r)return l;
int mid=l+r>>1;
cout<<"? "<<id<<" "<<k<<" "<<mid-l+1<<" ";for(int i=l;i<=mid;i++)cout<<i<<" ";
cout<<"\n";cout.flush();
int x;cin>>x;
if(x)return get(l,mid,k,id);
return get(mid+1,r,k,id);
}
bool ask(int x,int k){
cout<<"? "<<x<<" "<<k<<" "<<g.size()<<" ";
for(auto x:g)cout<<x<<" ";cout<<"\n";cout.flush();
int m;cin>>m;return m;
}
int c=63;
int main(){
cin>>n;c=n>8?(n+7)/8:1;
// g.push_back(1);
int k=get(1,n,n,1);
g.push_back(k);vis[k]=1;
for(int i=1;i<c;i++){
int x=get(1,n,1,g.back());if(vis[x]==1){
c=g.size();
for(int i=1;i<=n;i++)if(vis[i]==0&&ask(i,n-c))g.push_back(i);
sort(g.begin(),g.end());
cout<<"! "<<g.size()<<" ";for(auto x:g)cout<<x<<" ";return 0;
}
g.push_back(x);vis[x]=1;
}c=g.size();
for(auto x:g)vis[x]=1;
while(c<n){
int cnt=0;
for(int i=1;i<=n;i++)if(!vis[i]){
if(ask(i,c))g.push_back(i),vis[i]=1,++cnt;
}
if(cnt<c)break;
c+=c;
}
// if(c>n)c/=2;
for(int i=1;i<=n;i++)if(vis[i]==0&&ask(i,n))vis[i]=1,g.push_back(i);
sort(g.begin(),g.end());
cout<<"! "<<g.size()<<" ";for(auto x:g)cout<<x<<" ";
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!