A
题意
给出长度为n的数组,问是否可以经过不超过次交换相邻两值排序使该数组递增。
题解
我们知道冒泡排序的最坏情况复杂度即为,故只有在最坏情况下,即整个数组为严格递减序列时才能达到
次交换,所以只需要判断数组是否严格递减即可。
code
#include <bits/stdc++.h> using namespace std; int main() { int t; cin>>t; while(t--){ int n; cin>>n; int a[n+1]; for(int i = 0;i < n;++i){ cin>>a[i]; } int ok = 0; for(int i = 1;i < n;++i){ if(a[i] >= a[i-1]){ ok = 1; break; } } if(ok) puts("YES"); else puts("NO"); } return 0; }
B
题意
给出长度为n的数组,找到满足的数对数量。
题解
对于任意两个数,对两数中最大数的二进制最高位进行分析,当两数二进制最高位均为1时,则两数与操作后的值必然大于两数异或值,当两数二进制最高位不同时,则两数异或值必然大于两数与值。
举例: 5 101 2 010 5&2 = 000 5Xor2 = 111 5 101 4 100 5&4 = 100 5Xor4 = 001
可以看出,当两数对应二进制数的位数相同时,两数满足条件,否则不满足。
直接暴力枚举每段区间中数组中的个数即可(也可用二分优化),对每个区间内数的个数取组合数C(cnt,2)加和。
code
#include <bits/stdc++.h> #define reg register #define ll long long #define ull unsigned long long #define pii pair<int,int> #define inf 0x3f3f3f3f #define eps 1e-8 #define pi acos(-1.0) #define e exp(1.0) #define ios ios::sync_with_stdio(false) #define rep(i,l,r) for(int i=(l);i<(r);i++) using namespace std; const int maxn = 2e5 + 10; const int mod = 1e9 + 7; int main() { int t; cin>>t; while(t--){ int n; cin>>n; vector<ll> a(n); for(int i = 0;i < n;++i) scanf("%lld",&a[i]); sort(a.begin(),a.end()); ll res = 0; for(int i = 0;i < 32;++i){ int cnt = lower_bound(a.begin(),a.end(),(1ll<<(i+1))) - lower_bound(a.begin(),a.end(),(1ll<<(i))); res += 1ll * cnt *(cnt-1) / 2; } printf("%lld\n",res); } return 0; }
C1
题意
定义一个序列的权值为将奇数位值相加,减掉所有偶数位值
给出长度为n的序列,求一个权值最大的子序列。
题解
dp
dp[i][0]代表选第i个数作为第奇数个,dp[i][1]代表选第i个数作为第偶数个。
转移方程
code
#include <bits/stdc++.h> #define reg register #define ll long long #define ull unsigned long long #define pii pair<int,int> #define inf 0x3f3f3f3f #define eps 1e-8 #define pi acos(-1.0) #define e exp(1.0) #define ios ios::sync_with_stdio(false) #define rep(i,l,r) for(int i=(l);i<(r);i++) using namespace std; const int maxn = 3e5 + 10; const int mod = 1e9 + 7; ll dp[maxn][2]; int main() { int t; cin>>t; while(t--){ int n,q; cin>>n>>q; vector<ll> a(n); for(int i = 0;i < n;++i){ scanf("%lld",&a[i]); } dp[0][0] = a[0]; dp[0][1] = 0; for(int i = 1;i < n;++i){ dp[i][0] = max(dp[i-1][0],dp[i-1][1] + a[i]); dp[i][1] = max(dp[i-1][1],dp[i-1][0] - a[i]); } printf("%lld\n",max(dp[n-1][0],dp[n-1][1])); } return 0; }
C2
题意
在C1的基础上,对于每个序列存在q次交换,要求回答每次交换后的序列的最大子序列权值
题解
线段树贪心,
记录每个线段的左右端点值和当前段子序列的最大权值,合并时,由于子序列最大权值必定为奇数个,所以要减去两线段中间较小的值(即左儿子右端点和右儿子左端点中较小的值),以保证父序列权值值最大。
两种情况,若该值为已选中的值,则取消选中,否则,将其放到偶数位,用总权值减掉该值。但在更新时代码表示相同。
code
#include <bits/stdc++.h> #define reg register #define ll long long #define ull unsigned long long #define pii pair<int,int> #define inf 0x3f3f3f3f #define eps 1e-8 #define pi acos(-1.0) #define e exp(1.0) #define ios ios::sync_with_stdio(false) #define rep(i,l,r) for(int i=(l);i<(r);i++) using namespace std; const int maxn = 3e5 + 10; const int mod = 1e9 + 7; struct node{ ll res,l,r; }; ll a[maxn]; struct SegmentTree{ node tree[maxn << 2]; #define lson rt<<1,L,M #define rson rt<<1|1,M+1,R void pushup(int rt) { tree[rt].res = tree[rt<<1].res + tree[rt<<1|1].res; tree[rt].l = tree[rt<<1].l; tree[rt].r = tree[rt<<1|1].r; if(tree[rt<<1].r < tree[rt<<1|1].l) tree[rt].res -= tree[rt<<1].r; else tree[rt].res -= tree[rt<<1|1].l; // printf("-%d %d %d\n",tree[rt].res,tree[rt].l,tree[rt].r); } void build(int rt,int L,int R){ if(L == R){ tree[rt].l = tree[rt].res = tree[rt].r = a[L]; return ; } int M = L + R >> 1; build(lson); build(rson); pushup(rt); } void update(int rt, int L,int R,int k) { if(L == R){ tree[rt].l = tree[rt].r = tree[rt].res = a[k]; return ; } int M = L + R >> 1; if(k <= M)update(lson,k); else update(rson,k); pushup(rt); } }T; int main() { #ifndef ONLINE_JUDGE freopen("in","r",stdin); freopen("myout","w",stdout); #endif int t; cin>>t; while(t--){ int n,q; scanf("%d%d",&n,&q); for(int i = 0;i < n;++i){ scanf("%lld",&a[i+1]); } T.build(1,1,n); printf("%lld\n",T.tree[1].res); for(int i = 0;i < q;++i){ int l,r; scanf("%d%d",&l,&r); swap(a[l],a[r]); T.update(1,1,n,l); T.update(1,1,n,r); printf("%lld\n",T.tree[1].res); } } return 0; }
D
题意
给出n盏灯的允许打开时间,问一共打开k盏灯的集合有多少个。
题解
先按开灯时间l排序,再以关灯时间r作为排序条件建小根堆。
每次达到第i个的开灯时间,将第i盏灯加入堆,将已经关闭的灯弹出堆。
每次加入堆后判断是否满足k盏,为了不重复,刚刚加入的第i盏必须选择,在堆中其他的灯选择k-1个。
由于C(cnt-1,k-1)复杂度过高, 所以使用线性逆元预处理组合数即可。
code
#include <bits/stdc++.h> #define reg register #define ll long long #define ull unsigned long long #define pii pair<ll,ll> #define inf 0x3f3f3f3f #define eps 1e-8 #define pi acos(-1.0) #define e exp(1.0) #define ios ios::sync_with_stdio(false) #define rep(i,l,r) for(ll i=(l);i<(r);i++) using namespace std; const ll maxn = 1e6 + 10; const ll mod = 998244353; pii p[maxn]; ll inv[maxn]; ll fac[maxn]; bool cmp(pii a ,pii b){ return a.second < b.second; } ll qpow(ll a,ll b) { ll res = 1; while(b){ if(b & 1) res = res * a % mod; a = a * a % mod; b >>= 1; } return res; } void init() { fac[0] = 1; for(int i = 1;i <= maxn-1;++i){ fac[i] = fac[i-1] * i % mod; } inv[maxn-1] = qpow(fac[maxn-1],mod-2); for(int i = maxn - 2;i >= 0;--i){ inv[i] = inv[i+1] * (i + 1) % mod; } } ll C(ll n, ll m) { if(n < m) return 0; return fac[n] * inv[m] % mod * inv[n - m] % mod; } int main() { init(); ll n,k; cin>>n>>k; for(ll i = 0;i < n;++i){ scanf("%lld%lld",&p[i].second,&p[i].first); } sort(p,p+n,cmp); ll res = 0; priority_queue<pii,vector<pii>,greater<> > pq; for(ll i = 0;i < n;++i){ ll now = p[i].second; if(pq.size()){ pii hd = pq.top(); while(hd.first < now){ pq.pop(); if(pq.size())hd = pq.top(); else break; } } pq.push(p[i]); if(pq.size() >= k){ res = (res + (C(pq.size()-1,k-1)))%mod; } } printf("%lld\n",res); // system("pause"); return 0; }