快读(int)

C++快速输入技巧

快读

快读是一种输入方法,是一个自己敲出的函数,因为通过字符输入,比cin和scanf快上不少
今天忽然想起来自己还不会快读,于是一边水博客一边学

inline int read(){  
   int s=0,w=1;  
   char ch=getchar();  
   while(ch<='0'||ch>'9'){
        if(ch=='-')w=-1;
        ch=getchar();
        //如果读入的不是数字,而是-,w变为-1(变为负数)while(ch>='0'&&ch<='9'){
        s=s*10+ch-'0';
        ch=getchar();
        //之前读入23,现在读入2,s = 23 * 10 + 2 - 0 = 232return s*w;  
}  
## 题意 给定一个$1$到$n$的全排列,求一个全排列,使得其逆序对个数与原全排列相等,字典序大于原全排列且尽可能小 ## 思路 根据题意,我们可以知道要动顺序的数一定在一个右端点为$n$的区间中,那么我们可以枚举要动的区间的左端点,通过瞪眼法,我们看出,左端点即为从右往左数第一个数满足其右边至少有一个数比它大且其右边的逆序对个数不为$0$,然后我们就可以把此区间中逆序对个数求出,把区间中的数从小到大排列然后去构造逆序对。 ## 实现 求逆序对个数可以用树状数组维护(单点修改区间查询一个数右边比它小的数的个数)。为了让字典序尽可能小,把左端点数值与区间中比它大的最小的数互换,把左端点踢出去,然后把区间中数从小到大排列,把最大的那些数反过来,这样我们就得到了很多逆序对。最后取一个比较大的数放在这坨降序排列的数前面,这样就可以使得逆序对个数相同的情况下字典序最小(你问我比原来的字典序小怎么办,其实最开始我们就把左端点变大之后踢了出去,所以不用管它),然后就做完了,嗯,对。 ## 代码 ``` #include <bits/stdc++.h> using namespace std; const int N = 5e5 + 10; int n; int tree[N], input[N]; long long num[N]; int lowbit(int x){ return x & -x; } void add(int x){//单点修改 while(x <= n){ tree[x]++; x += lowbit(x); } return; } int cha(int x){//区间查询 int ans = 0; while(x){ ans += tree[x]; x -= lowbit(x); } return ans; } int read(){// int s = 0; char ch = getchar(); while(ch > '9' || ch < '0') ch = getchar(); while(ch <= '9' && ch >= '0'){ s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar(); } return s; } int main() { cin >> n; for(int i = 1; i <= n; i++){//入原序列 input[i] = read(); } for(int i = n; i >= 1; i--){//求每个点右侧比它小的数的个数 num[i] = cha(input[i]); add(input[i]); } int l = 0; int ma = 0; long long now = 0; for(int i = n; i >= 1; i--){//枚举左端点位置 if(input[i] < ma && now){ l = i; break; } now += num[i]; ma = max(ma, input[i]); } sort(input + l + 1, input + n + 1); //将数还原为初始状态(注意左端点不能排进去) int zz = l + 1; while(zz <= n && input[zz] < input[l]) zz++;//找到此时左端点应被修改为的数 swap(input[zz], input[l]); long long ge = 1; while(ge * (ge + 1) < now * 2) ge++;//求要被修改的末尾的数的个数 int shu = now - (ge - 1) * ge / 2 - 1;//求还差多少个逆序对没被满足 l = n - ge + 1; int r = n; if(shu <= 0){ while(l < r){//将升序改为降序以尽可能用最少的数构造最多的逆序对个数 swap(input[l], input[r]); l++; r--; } } else{ ge++; l = n - ge + 1; int ls = input[l + shu];//交换点使其逆序对个数满足条件 for(int i = l + shu - 1; i >= l; i--){ input[i + 1] = input[i]; } input[l] = ls; l++; while(l < r){//与66行同理 swap(input[l], input[r]); l++; r--; } } for(int i = 1; i <= n; i++){//输出结果 cout << input[i] << " "; } return 0; } ``` 以上是我写的题解,帮我优化一下使得其逻辑通顺,且更易理解
最新发布
11-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值