题目描述:
将给出长为n的数组排序为不减序列,每次只能交换相邻位置的数,问能否在
n
(
n
−
1
)
2
−
1
\frac{n(n-1)}2-1
2n(n−1)−1次以内完成。
题目解析:
对数组排序的过程实际上就是一个冒泡排序的过程。冒泡排序的最坏复杂度是
O
(
n
2
)
O(n^2)
O(n2),此时的序列是降序的,执行交换的次数恰好为
n
(
n
−
1
)
2
\frac{n(n-1)}2
2n(n−1)。所以这个题就转变为了求:数组是否为降序序列。
但是这里还有一个问题没有解决:如果数组中有重复元素应当如何处理?
以序列
{
5
,
4
,
4
,
2
,
1
}
\{5,4,4,2,1\}
{5,4,4,2,1}为例,该序列等同于
{
5
,
3
,
4
,
2
,
1
}
\{5,3,4,2,1\}
{5,3,4,2,1},不满足上述条件,所以输出YES
#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return
#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;
const int maxn = 5e4+5;
int t,len;
int store[maxn];
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL);
cin>>t;
while(t--){
cin>>len;
rep(i,0,len)
cin>>store[i];
bool flag = true;
rep(i,1,len){
flag = flag & (store[i-1]>store[i]);
}
if(flag){
cout<<"NO"<<endl;
}else{
cout<<"YES"<<endl;
}
}
re 0;
}
Problem B
题目描述:
给出一个序列
{
a
n
}
\{a_n\}
{an},找出序列中有序对
(
i
,
j
)
(i,j)
(i,j)的个数。其中要求
i
<
j
i<j
i<j,且
a
i
&
a
j
≥
a
i
⊕
a
j
a_i \& a_j \geq a_i \oplus a_j
ai&aj≥ai⊕aj
题目解析:
在开始之前先定义一个名词:记二进制表示下,从最高位起,第一位不为零的位置到最低位的长度为位长度。如3的二进制为11,位长度为2;4的二进制为100,位长度为3。
对于AND来说,同为真;对于XOR来说,异为真。由此我们可知,满足上述条件的
a
i
,
a
j
a_i,a_j
ai,aj必须位长度相同,当且仅当
a
i
=
=
a
j
a_i==a_j
ai==aj时取等号,否则一定是
a
i
&
a
j
<
a
i
⊕
a
j
a_i\&a_j < a_i \oplus a_j
ai&aj<ai⊕aj
#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return
#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;
const int maxn = 1e5+5;
int t,n;
ll sumNum[maxn][32];
int start[maxn];
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL);
cin>>t;
while(t--){
mem(sumNum,0);
cin>>n;
rep(i,0,n){
int tmp;
cin>>tmp;
int pos = -1;
while(tmp){
tmp = tmp>>1;
pos ++;
}
start[i] = pos;
sumNum[i][pos] ++;
}
ll ans = 0;
rep(i,1,n)
rep(j,0,31){
sumNum[i][j] += sumNum[i-1][j];
if(j==start[i])
ans += sumNum[i][j] - 1;
}
cout<<ans<<endl;
}
re 0;
}
Problem C1
题目描述:
从数组
{
a
n
}
\{a_n\}
{an}中挑一个子序列
{
b
1
,
b
2
,
⋯
,
b
k
}
\{b_1,b_2,\cdots,b_k\}
{b1,b2,⋯,bk},其中
1
≤
b
1
<
b
2
<
⋯
<
b
k
≤
n
1\leq b_1<b_2<\cdots <b_k\leq n
1≤b1<b2<⋯<bk≤n,求和
a
b
1
−
a
b
2
+
a
b
3
−
a
b
4
+
⋯
a_{b_1}-a_{b_2}+a_{b_3}-a_{b_4}+\cdots
ab1−ab2+ab3−ab4+⋯最大值。
题目解析:
注意:这里挑选的下标不需要是连续的,只要符合下标递增即可。
记dp[i][0]表示在下标
i
i
i之前,最后一个元素为+时能得到的最大值
记dp[i][1]表示在下标
i
i
i之前,最后一个元素为-时能得到的最大值
初始化 dp[0][0] = store[0],dp[0][1] = 0
递推式
d
p
[
i
]
[
0
]
=
m
a
x
(
d
p
[
i
−
1
]
[
0
]
,
s
t
o
r
e
[
i
]
,
d
p
[
i
−
1
]
[
1
]
+
s
t
o
r
e
[
i
]
)
d
p
[
i
]
[
1
]
=
m
a
x
(
d
p
[
i
−
1
]
[
1
]
,
0
,
d
p
[
i
−
1
]
[
0
]
−
s
t
o
r
e
[
i
]
)
dp[i][0] = max(dp[i-1][0],store[i],dp[i-1][1]+store[i]) \\ dp[i][1] = max(dp[i-1][1],0,dp[i-1][0]-store[i])
dp[i][0]=max(dp[i−1][0],store[i],dp[i−1][1]+store[i])dp[i][1]=max(dp[i−1][1],0,dp[i−1][0]−store[i])
#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return
#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;
const int maxn = 3e5+5;
int t;
int n,q;
int store[maxn];
ll dp[maxn][2];
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL);
cin>>t;
while(t--){
cin>>n>>q;
rep(i,0,n){
cin>>store[i];
}
dp[0][0] = store[0],dp[0][1] = 0;;
rep(i,1,n){
dp[i][0] = max(dp[i-1][1]+store[i],(ll)store[i]);
dp[i][0] = max(dp[i][0],dp[i-1][0]);
dp[i][1] = max(dp[i-1][0]-store[i],0LL);
dp[i][1] = max(dp[i][1],dp[i-1][1]);
}
cout<<max(dp[n-1][0],dp[n-1][1])<<endl;
}
re 0;
}
三个小时的血泪教训,XOR不能交换值相同的变量
#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return
#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;
template<class T> void _deb(const char *name,T val){
cout<<name<<val<<endl;
}
const int MAXN = 3e5+5;
int t;
int n,q;
ll ans;
int store[MAXN];
set<int> localMax,localMin;
void viewModel(int pos);
inline bool isLocalMax(int pos);
inline bool isLocalMin(int pos);
void checkLocal(int num);
void LocalMax(int pos);
void LocalMin(int pos);
int main(){
scanf("%d",&t);
while(t--){
ans = 0;
localMax.clear();localMin.clear();
scanf("%d %d",&n,&q);
rep(i,1,n+1)
scanf("%d",&store[i]);
store[0] = store[n+1] = -1;
rep(i,1,n+1)
viewModel(i);
printf("%lld\n",ans);
rep(i,0,q){
int posA,posB;
scanf("%d %d",&posA,&posB);
//swap
swap(store[posA],store[posB]);
rep(_,max(posA-1,1),min(posA+2,n+1)){
checkLocal(store[_]);
viewModel(_);
}
rep(_,max(posB-1,1),min(posB+2,n+1)){
checkLocal(store[_]);
viewModel(_);
}
printf("%lld\n",ans);
}
}
re 0;
}
void viewModel(int pos){
if(isLocalMax(pos))
LocalMax(pos);
if(isLocalMin(pos)){
LocalMin(pos);
}
}
inline bool isLocalMax(int pos){
return store[pos]>store[pos-1] && store[pos]>store[pos+1];
}
inline bool isLocalMin(int pos){
return store[pos]<store[pos-1] && store[pos]<store[pos+1];
}
void checkLocal(int num){
if(localMin.find(num)!=localMin.end()){
localMin.erase(num);
ans += num;
}
if(localMax.find(num)!=localMax.end()){
localMax.erase(num);
ans -= num;
}
}
void LocalMax(int pos){
int &num = store[pos];
localMax.insert(num);
ans += num;
}
void LocalMin(int pos){
int &num = store[pos];
localMin.insert(num);
ans -= num;
}
Problem D
题目描述:
给你n个灯,你知道每个灯亮的时间,现在需要k个灯同时亮,问有多少种可能的方案
题目解析:
深深折服于我弱鸡一样的代码实现
定义灯的
l
l
l为开始时间,
r
r
r为结束时间。
按时间遍历,到达某个开始时间将灯加入集合,并计算包含加入元素的所有排列方式;到达结束时间则将灯从集合中移除。
按照以上思想,我们可以首先将灯按开始时间升序排序(结束时间可以不做处理),且由于仅在开始时间计算数值,所以不必遍历全部时间,只需要处理所有的开始时间点即可。
由于我们按升序加入灯,所以集合中结束时间越早的灯越容易被移除,故选择最小堆维护集合。
在每次加入前,将集合中已经熄灭的灯移除,再加入新灯,计算与此相关的所有排列数。
#pragma GCC diagnostic error "-std=c++11"
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<int,int>
#define re return
#define getLen(name,index) name[index].size()
#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;
const int maxn = 3e5+5;
const int MOD = 998244353;
const int MAXN = 3e5+5;
LL da[MAXN];//G++ long long
void init()
{
int i;
da[0]=1;
da[1]=1;
for(i=2;i<MAXN;i++)
da[i]=i*da[i-1]%MOD;
}
LL quickmod(LL a,LL b)
{
LL ans=1;
while(b)
{
if(b&1)
{
ans=(ans*a)%MOD;
b--;
}
b/=2;
a=((a%MOD)*(a%MOD))%MOD;
}
return ans;
}
LL C(LL a, LL b) //C(a,b)
{
if(b>a)
re 0LL;
return (da[a]%MOD)*(quickmod(da[b]*da[a-b]%MOD,MOD-2))%MOD;
}
struct Lamp{
int start,finish;
};
int n,k;
Lamp lamp[maxn];
priority_queue<int,vector<int>,greater<int> > Q;
inline bool cmp(const Lamp &a,const Lamp &b);
int main(){
init();
scanf("%d %d",&n,&k);
rep(i,0,n){
scanf("%d %d",&lamp[i].start,&lamp[i].finish);
}
sort(lamp,lamp+n,cmp);
int ans = 0,i = 0;
while(i<n){
int nowTime = lamp[i].start;
while(!Q.empty() && Q.top()<nowTime){
Q.pop();
}
int num = Q.size();
Q.push(lamp[i++].finish);
ans = (ans + C((LL)num,(LL)k-1))%MOD;
}
printf("%d\n",ans);
re 0;
}
inline bool cmp(const Lamp &a,const Lamp &b){
re a.start<b.start;
}