题解
今天这个打 NOI.AC 的模拟题,打得我心里发毛,这个要是到联赛我不是爆零?
题目质量蛮高的,官方给出的题解就比较的随意了(其实他给我markdown写好了233)
第一题——排队
【题目描述】
- 给出长度为n的数列,以及目标中位数x,要求改变数列元素大小,使得最终数列中位数为x,修改1的代价为1,求修改代价和最小是多少
- 这个水题
- 就排个序,贪心修改就好了(这个不是显然?),然后记得开long long
#include <bits/stdc++.h>
#define LL long long
using namespace std;
int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x;
}
const int N=2e5+10;
int n,x;
int a[N];
int main(){
n=read(),x=read();
for(int i=1;i<=n;i++)
a[i]=read();
sort(a+1,a+n+1);
LL ans=0;
for(int i=1;i<=n;i++){
if(i<(n+1)/2&&a[i]>x)ans+=a[i]-x;
if(i==(n+1)/2&&a[i]!=x) ans+=abs(a[i]-x);
if(i>(n+1)/2&&a[i]<x)ans+=x-a[i];
}
cout<<ans;
}
第二题——翘课
【题目描述】
- 给出n个节点的图,会加入m条边,给出条件k,初始无连边,求出每加入一条边之后有多少节点的相邻节点的相邻节点的相邻节点……个数大于等于k
- 这道题我卡了两个小时,一直在想在线做法,但是没有搞出来。
- std是离线算法,考虑先把图建好,然后倒着删边,每删掉一条边,边上的节点度数减一,它的节点响铃的节点数的入度都减一,如果图建好之后的度数还是比K小,那么他就不能对答案做出贡献
#include <bits/stdc++.h>
using namespace std;
int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x;
}
const int N=200010;
struct Edge{
int nxt,to,id;
}edge[N<<1];
int head[N],tot=0;
int d[N];
bool deleted[N],in[N];
int u[N],v[N];
int n,m,k,ans[N],ss;
void add(int u,int v,int id){
edge[++tot].to=v;
edge[tot].nxt=head[u];
edge[tot].id=id;
head[u]=tot;
}
void dfs(int u){
if((d[u]>=k)||(in[u]==0)) return;
ss--;
in[u]=0;
for(int i=head[u];i;i=edge[i].nxt){
if(in[edge[i].to]&&deleted[edge[i].id]==0){
d[edge[i].to]--;
dfs(edge[i].to);
}
}
}
int main(){
n=read(),m=read(),k=read();
for(int i=1;i<=m;i++){
u[i]=read(),v[i]=read();
add(u[i],v[i],i);
add(v[i],u[i],i);
d[u[i]]++,d[v[i]]++;
}
ss=n;
for(int i=1;i<=n;i++) in[i]=1;
for(int i=1;i<=n;i++) dfs(i);
for(int i=m;i>=1;i--){
ans[i]=ss;
deleted[i]=1;
if(in[v[i]]) d[u[i]]--;
if(in[u[i]]) d[v[i]]--;
dfs(u[i]);
dfs(v[i]);
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
第三题——运气大战
【题目描述】
给出数列
a
,
r
a,r
a,r,给出m次操作交换
c
i
,
c
j
c_i,c_j
ci,cj,求出对于每次交换后
max
a
i
∗
r
j
\max{a_i*r_j}
maxai∗rj
其中满足:
a
,
r
a,r
a,r为任意组合,只要满足
i
≠
j
i\ne j
i̸=j
-
一直在硬钢第二题,没空打第三题的暴力
-
由于排序不等式,我们尽量想顺序放。两边都排序。
-
由于 n 个不能配的干扰,又不能完全顺序放。
-
有个结论,最后匹配出第 i 个人的运气值是第 j 个的话, ∣ i − j ∣ ≤ 2 |i-j|\le2 ∣i−j∣≤2。这个结论从最小化逆序对的个数来看,自己把附近几个线连起来画一画证明一下。(四个的话可以前后两个分两组交换,往上以此类推)
-
然后暴力修改更新,打个dp就可以了
#include <bits/stdc++.h>
#define LL long long
using namespace std;
int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x;
}
const int N=30100;
int n,q;
bool visited[N];
struct node{
int val,id;
bool operator > (const node t) const{return val>t.val;}
bool operator < (const node t) const{return val>t.val;}
}a[N],rr[N];
int ca[N],cr[N];
LL f[N][2],dp[N];
const LL INF=-3.1e16;
inline LL match(int aa,int b){
if(aa<=0||aa>n||b<0||b>n) return INF;
return (LL)a[aa].val*rr[b].val;
}
inline void update(int pos){
if(pos<=0||pos>n) return;
f[pos][0]=match(pos+1,pos)+match(pos,pos+1);
f[pos][1]=max(match(pos,pos+1)+match(pos+1,pos+2)+match(pos+2,pos),match(pos,pos+2)+match(pos+1,pos)+match(pos+2,pos+1));
}
int main(){
n=read(),q=read();
for(int i=1;i<=n;i++) a[i].val=read(), a[i].id=i;
for(int i=1;i<=n;i++)rr[i].val=read(),rr[i].id=i;
sort(a+1,a+n+1);
sort(rr+1,rr+n+1);
for(int i=1;i<=n;i++)ca[a[i].id]=cr[rr[i].id]=i;
for(int i=1;i<=n;i++) update(i);
while(q--){
int l,r;
l=read(),r=read();
swap(rr[cr[l]].id,rr[cr[r]].id);
swap(cr[l],cr[r]);
for(int i=-2;i<=0;i++){
update(ca[l]+i);
update(cr[l]+i);
update(ca[r]+i);
update(cr[r]+i);
}
dp[n+1]=0;
for(int i=n;i;i--){
dp[i]=max(dp[i+2]+f[i][0],dp[i+3]+f[i][1]);
if(a[i].id!=rr[i].id) dp[i]=max(dp[i],(LL)a[i].val*rr[i].val+dp[i+1]);
}
printf("%lld\n",dp[1]);
}
}