其实是一场相对比较傻逼的场,但因为自己更傻逼只出了3题,还得继续努力呀
A.Cubes Sorting
思路:冒泡排序的最坏次数就是n*(n-1)/2, 只要当单增或者单减才能取到,所以就是刚好完全单减的时候是不行的
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e4+5;
int a[maxn];
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
int i;
for(i=2;i<=n;++i)
if (a[i]>=a[i-1])
break;
if (i<=n)
puts("YES");
else puts("NO");
}
return 0;
}
B.Rock and Lever
思路:
容易发现只有当最高位相同的两个数才能产生贡献,所以就用桶存下来每一位cnti 处理这个偏序
答案就是
∑
C
c
n
t
i
2
\sum C_{cnti}^{2}
∑Ccnti2
复杂度 O(32n)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int a[maxn],cnt[34];
void ins(int x){
int ans=0;
while(x){
ans++;
x>>=1;
}
cnt[ans]++;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(cnt,0,sizeof(cnt));
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
for(int i=1;i<=n;++i)ins(a[i]);
ll ans=0;
for(int i=1;i<=32;++i){
ans+=1ll*cnt[i]*(cnt[i]-1)/2;
}
cout<<ans<<endl;
}
return 0;
}
C.Pokémon Army
本场最傻逼的题来了,但我更傻逼
一眼知道是极值点产生贡献,但是开头结尾边界没考虑好,其实只要将a[0],a[n+1]都赋值为负无穷就好了
交换操作其实是个更傻逼的问题,当时已经想到了该贡献明显只和极值点以及相邻的2个数有关,但是没写,只要删除之前的贡献,再加上去就好了,其实每次操作只有6个点可能被修改,题目相当于需要我们撤销+重新贡献而已
时间复杂度大概是O(q log)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+5;
int a[maxn],n,q;
bool v[maxn];
ll ans=0;
void del(int x){
if(!v[x])return;
v[x]=0;
if(a[x]<a[x-1]&&a[x]<a[x+1])ans+=a[x];
else if(a[x]>a[x-1]&&a[x]>a[x+1])ans-=a[x];
}
void add(int x){
if(v[x])return;
v[x]=1;
if(a[x]<a[x-1]&&a[x]<a[x+1])ans-=a[x];
else if(a[x]>a[x-1]&&a[x]>a[x+1])ans+=a[x];
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
ans=0;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i)scanf("%d",&a[i]),v[i]=0;
a[n+1]=a[0]=0;
for(int i=1;i<=n;++i){
if(a[i]>a[i-1]&&a[i]>a[i+1])ans+=a[i],v[i]=1;
else if(a[i]<a[i-1]&&a[i]<a[i+1])ans-=a[i],v[i]=1;
}
cout<<ans<<endl;
for(int i=1;i<=q;++i){
int l,r;
scanf("%d%d",&l,&r);
if(l==r){cout<<ans<<endl;continue;}
del(l-1);del(l);del(l+1);
del(r-1);del(r);del(r+1);
swap(a[l],a[r]);
add(l-1);add(l);add(l+1);
add(r-1);add(r);add(r+1);
cout<<ans<<endl;
}
}
return 0;
}
D.Rescue Nibel!
思路:组合数+优先队列
比赛的时候一直在想线段树怎么写,因为图论和数据结构被队友接手后基本没写过了,实际上这是一个DP的思想,计数不重不漏的关键还是在于找一个基准点,经常都是通过枚举来划分的,我有想过将当前区间作为第一个区间,这样必当不重,但是却很难维护,其实将当前区间作为最后一个区间即可,对于当前区间的左端点,前面所有区间在其右边的左端点都会产生贡献,用优先队列维护右端点即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+5;
const ll mod=998244353;
int l[maxn],r[maxn],mx[maxn<<1],L,R;
ll fac[maxn],facinv[maxn],ans=0;
vector<pair<int,int>>a;
priority_queue<int,vector<int>,greater<int>>q;
ll mypow(ll a,ll b){
ll ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
void init(){
fac[1]=fac[0]=1;
for(int i=2;i<maxn;++i)fac[i]=fac[i-1]*i%mod;
facinv[maxn-1]=mypow(fac[maxn-1],mod-2);
for(int i=maxn-2;i>=0;--i)
facinv[i]=facinv[i+1]*(i+1)%mod;
}
ll C(int n,int m){
return fac[n]*facinv[n-m]%mod*facinv[m]%mod;
}
int main(){
init();
int n,k,cnt=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i){
scanf("%d%d",&L,&R);
a.push_back({L,R});
}
sort(a.begin(),a.end());
for(int i=0;i<a.size();++i){
while(!q.empty()&&q.top()<a[i].first)q.pop();
if(q.size()>=k-1)
ans=(ans+C(q.size(),k-1))%mod;
q.push(a[i].second);
}
cout<<ans<<endl;
return 0;
}
E.Battle Lemmings
题意:
给你一个01串,你可以进行k次操作,每次将一个1和相邻的0互换,问最大的对数,对数就是一个1左右两边0的对数之和
容斥思想+DP
思路:一道非常不错的DP,其实并没有那么难,状态我都已经猜到了,但是由于转化不够好,导致最后没法定义下来
我们可以发现,其实1划分了n个数,出现了很多段0区间,不管怎么操作,1的个数都是不变的,所以枚举1的位置是必须的,关键容斥转化来了
s
u
m
=
n
(
n
−
1
)
2
−
(
c
n
t
−
1
)
c
n
t
2
−
c
n
t
∗
(
n
−
c
n
t
)
−
∑
C
i
2
sum= \cfrac{n(n-1)}{2}-\cfrac{(cnt-1)cnt}{2}-cnt*(n-cnt)-\sum C_{i}^{2}
sum=2n(n−1)−2(cnt−1)cnt−cnt∗(n−cnt)−∑Ci2
i表示每段连续0的个数,最后一项越小总和越大
所以dp[i][j][k]表示枚举到第i个1,放在第j个位置,做了k次操作的最后一项的最小值
d p [ i ] [ j ] [ k + ∣ p o s i − j ∣ ] = 0 < = t < j m i n ( d p [ i ] [ j ] [ k + ∣ p o s i − j ∣ ] , d p [ i − 1 ] [ t ] [ k ] + C j − t + 1 2 dp[i][j][k+|pos_i-j|]=_{0<=t<j}min(dp[i][j][k+|pos_i-j|],dp[i-1][t][k]+C_{j-t+1}^{2} dp[i][j][k+∣posi−j∣]=0<=t<jmin(dp[i][j][k+∣posi−j∣],dp[i−1][t][k]+Cj−t+12
虽然看起来是O(n^5)的,似乎是8e8左右,题解说能跑到O(n ^5/27),所以冲一波
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[85],pos[85],cnt;
int dp[82][82][82*82];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
if(a[i])pos[++cnt]=i;
}
int ans=0;
int N=n*(n-1)/2;
ans=n*(n-1)/2-cnt*(cnt-1)/2-cnt*(n-cnt);
for(int i=0;i<=cnt;++i)
for(int j=0;j<=n;++j)
for(int k=0;k<=N;++k)dp[i][j][k]=0x3f3f3f3f;
int sum=0;
for(int i=0;i<=cnt;++i){
if(i>=1)
sum+=max(0,(pos[i]-pos[i-1]-1)*(pos[i]-pos[i-1]-2)/2);
dp[i][pos[i]][0]=sum;
}
for(int i=1;i<=cnt;++i)
for(int j=1;j<=n;++j)
for(int t=0;t<j;++t)
for(int k=0;k<=N;++k){
if(dp[i-1][t][k]==0x3f3f3f3f)continue;
dp[i][j][k+abs(pos[i]-j)]=min(dp[i][j][k+abs(pos[i]-j)],dp[i-1][t][k]+max(0,(j-t-1)*(j-t-2)/2));
}
int mx=0x3f3f3f3f;
for(int i=0;i<=N;++i){
for(int j=1;j<=n;++j){
mx=min(mx,dp[cnt][j][i]+max(0,(n-j-1)*(n-j)/2));
}
if(!cnt){printf("0 ");continue;}
printf("%d ",ans-mx);
}
return 0;
}