CSP 模拟 6
T1 排序
好像是简单题,考虑这个排列 \(\pi\) 的逆排列 \(\pi^{-1}\)(如果排列是 \(a_i\),则逆排列为 \(b_{a_i}=i\)),因为逆序对的定义是序列编号和数值大小关系不一样,所以在逆排列中逆序对和原排列相同,对逆排列做冒泡排序,因为冒泡排序交换的是相邻值的位置,对其他值没有影响,所以对逆排列冒泡排序的所有逆序对即为答案
点击查看代码
#include<bits/stdc++.h>
#define N 1005
#define pii pair<int,int>
#define mp make_pair
using namespace std;
int n,a[N],b[N],tot,pos[N];
pii ans[N*N];
// 爆零就爆零,天天好心情
int main(){
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d",a+i),b[a[i]]=i;
for(int i=1;i<=n;i++)
for(int j=1;j<=n-i;j++)
if(b[j]>b[j+1]){
swap(b[j],b[j+1]);
ans[++tot]=mp(b[j],b[j+1]);
}
cout<<tot<<endl;
for(int i=1;i<=tot;i++)
printf("%lld %lld\n",ans[i].first,ans[i].second);
}
T2 Xorum
很有意思的构造题
对于这个奇数 \(x\) 而言,我们不断把它的最高位消去即可得到 1,所以考虑如何得到这个最高位
构造方案(建议模拟一遍):
- 将 \(x\) 最高位和最低位对齐,设这个数为 \(y\)
- \(x\oplus y\) 得到一个数为 \(z\),则这个数中只有最高位被消掉
- \(z+y\) 构造出来一个 \(sum\),则最高位被补上,\(y\) 除最低位均左移一位
- 将 \(sum\oplus x \oplus y*2\) 则只剩下最高位加一位
- 用最高位加一位消掉 \(y\) 其他的得到最高位
点击查看代码
#include<bits/stdc++.h>
#define N 1000005
#define int long long
using namespace std;
int x,a[N],vis[64],tot;
struct node{int typ,a,b;}ans[N];
unordered_map<int,bool> exi;
// 2500 2500 2800 3200
signed main(){
cin>>x;
assert(x!=262143);
while(true){
if(x==1) break;
int highbit=0;
for(int i=20;i>=0;i--)
if(x&(1ll<<i)){
highbit=i;
break;
}
int y=x;
for(int i=0;i<highbit;i++){
ans[++tot]=(node){1,y,y};
y<<=1;
}
if(!vis[highbit+1]){
ans[++tot]=(node){2,y,x};
int tmp=y^x;
ans[++tot]=(node){1,tmp,y};
int temp=tmp+y;
ans[++tot]=(node){2,temp,x};
temp^=x;
ans[++tot]=(node){1,y,y};
ans[++tot]=(node){2,y*2,temp};
vis[highbit+1]=1;
}
for(int i=highbit+1;(1ll<<i)<=y;i++){
if(!vis[i]){
ans[++tot]=(node){1,(1ll<<(i-1)),(1ll<<(i-1))};
vis[i]=true;
}
if(y&(1ll<<i)){
ans[++tot]=(node){2,y,(1ll<<i)};
y^=(1ll<<i);
}
}
vis[highbit]=true;
ans[++tot]=(node){2,x,(1ll<<highbit)};
x^=(1ll<<highbit);
}
cout<<tot<<endl;
for(int i=1;i<=tot;i++)
printf("%lld %lld %lld\n",ans[i].typ,ans[i].a,ans[i].b);
}
T3 有趣的区间问题
序列分治,将序列分成两个部分 \([l,mid],(mid,r]\),然后只考虑跨越中点的区间答案,递归下去
定义 \(max(l,r)=\max_{i=l}^ra_i\),\(min(l,r)=\min_{i=l}^ra_i\)
双指针,设当前走到的左端点为 \(L\),定义 \(p1\) 为满足 \(max(mid+1,R)\not=max(L,R)\and min(mid+1,R)\not=min(L,R)\) 的 \(R\) 的最大值,\(p2\) 为满足 \(max(mid+1,R)\not=max(L,R)\or min(mid+1,R)\not=min(L,R)\) 的 \(R\) 的最大值,对于以当前区间为左端点 \(L\) 的情况分类讨论:
- \(R\in (mid,p1]\) ,\(\max\) 和 \(\min\) 只和左区间相关,如果左区间 \(\max\) 和 \(\min\) \(popcount\) 相等则统计区间长度
- \(R\in (p1,p2]\),对于左侧的 \(\max\) 和 \(\min\) 开桶维护即可
- \(R\in (p2,r]\) 直接维护右区间对于 \(popcount\) 相等的后缀和即可
点击查看代码
#include<bits/stdc++.h>
#define N 1000005
#define int long long
#define bitcnt __builtin_popcountll
using namespace std;
int n,a[N],cnt[N],ans,mx[N],mn[N];
int buk1[64],buk2[64];
void solve(int l,int r){
if(l==r){ans++;return;}
int mid=(l+r)>>1;
memset(buk1,0,sizeof(buk1));
memset(buk2,0,sizeof(buk2));
mx[mid]=mn[mid]=mid;mx[mid+1]=mn[mid+1]=mid+1;
for(int i=mid+2;i<=r;i++)
mx[i]=a[i]>a[mx[i-1]]?i:mx[i-1],
mn[i]=a[i]<a[mn[i-1]]?i:mn[i-1];
for(int i=mid-1;i>=l;i--)
mx[i]=a[i]>a[mx[i+1]]?i:mx[i+1],
mn[i]=a[i]<a[mn[i+1]]?i:mn[i+1];
int p1=mid+1,p2=mid+1;
for(int i=mid;i>=l;i--){
while(p1<=r&&a[mx[i]]>=a[mx[p1]])
buk1[bitcnt(a[mn[p1++]])]++;
while(p2<=r&&a[mn[i]]<a[mn[p2]])
buk2[bitcnt(a[mn[p2++]])]++;
if(cnt[mx[i]]==cnt[mn[i]]) ans+=min(p1,p2)-mid-1;
if(p2<p1) ans+=buk1[cnt[mx[i]]]-buk2[cnt[mx[i]]];
}
p1=p2=mid;
memset(buk1,0,sizeof(buk1));
memset(buk2,0,sizeof(buk2));
for(int i=mid+1;i<=r;i++){
while(p1>=l&&a[mx[i]]>a[mx[p1]])
buk1[bitcnt(a[mn[p1--]])]++;
while(p2>=l&&a[mn[i]]<=a[mn[p2]])
buk2[bitcnt(a[mn[p2--]])]++;
if(cnt[mx[i]]==cnt[mn[i]]) ans+=mid-max(p1,p2);
if(p1<p2) ans+=buk1[cnt[mx[i]]]-buk2[cnt[mx[i]]];
}
solve(l,mid);solve(mid+1,r);
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++)
scanf("%lld",a+i),
cnt[i]=bitcnt(a[i]);
solve(1,n);
cout<<ans;
}
T4 无聊的卡牌问题
考虑贪心,塞进一个栈里,如果顶端有三个相同先后手的点,则弹出,将三个点压成一个点连上栈顶节点,表示比它后取,然后拓扑排序即可,但是需要注意的是,必须留一个后手的根,避免出现错误,正确性证明考虑每次拿一个先手或后手产生的影响
点击查看代码
#include<bits/stdc++.h>
#define N 1205
using namespace std;
int n,visf[N],stk[N],top,id[N],tot,Cnt[N];
int fuck[N][4],fa[N],typ[N],deg[N],vis[N];
inline int read(){
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int main(){
cin>>n;
for(int i=1;i<=n*3;i++){int x=read();visf[x]=1;}
for(int i=1;i<=n*6;i++){
if(!top||typ[stk[top]]!=visf[i]){
typ[++tot]=visf[i];
stk[++top]=tot;
fuck[tot][++Cnt[tot]]=i;
}
else{
fuck[stk[top]][++Cnt[stk[top]]]=i;
if(Cnt[stk[top]]==3) fa[stk[top]]=stk[top-1],top--;
}
}
// for(int i=1;i<=tot;i++)
// printf("%d %d %d %d %d\n",fa[i],typ[i],fuck[i][1],fuck[i][2],fuck[i][3]);
int cnt=0;
for(int i=1;i<=tot;i++){
if(!fa[i]&&!typ[i]) cnt++;
deg[fa[i]]++;
}
int now=1;
// cout<<tot<<endl;
for(int i=1;i<=tot;i++){
for(int j=1;j<=tot;j++)
if(!deg[j]&&typ[j]==now&&!vis[j]){
if(cnt==1&&i<tot&&(!fa[j])&&(!typ[j])) continue;
vis[j]=1;deg[fa[j]]--;
if(!typ[j]&&!fa[j]) cnt--;
printf("%d %d %d\n",fuck[j][1],fuck[j][2],fuck[j][3]);
break;
}
now^=1;
}
}