1. 程式人生 > >NOI.AC 20181103 題解

NOI.AC 20181103 題解

dash 身高 clu 順序 med pan 獲得 reset include

CF 1037B Reach Median

班上 n個同學(n 是奇數)排成一排站隊,為了美觀,需要大家高度的中位數是 x。

你可以讓同學們在腳下墊木板或者稍微蹲一點來達成這個目標。對任意一位同學的身高減少或者增加1的代價都是1。問

你最少花費多少代價可以讓最後大家高度的中位數是x。

solution:
基於一個貪心,現將序列排序。設當前的中位數為MID,位置是pos

那麽如果MID比x大,那麽將MID=x計算代價,我們的中位數要左移然後就考慮將pos左側所有小於x的數全部賦值為x計算代價

如果MID比x小,那麽將MID=x計算代價,我們中位數要右移就考慮將pos右側所有大於x的數全部賦值為x計算代價

然後輸出答案即可。

Code:

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int MAXN=2e5+10;
int a[MAXN],n,x;
inline int read()
{
    int X=0,w=0; char c=0;
    while (!(c>=0&&c<=9)) w|=c==-,c=getchar();
    while (c>=0&&c<=9) X=(X<<1
)+(X<<3)+(c^48),c=getchar(); return w?-X:X; } inline void write(int x) { if (x<0) { x=-x;putchar(-);} if (x>9) write(x/10); putchar(0+x%10); } inline void writeln(int x){write(x);putchar(\n);} signed main() { n=read(); x=read(); for (int i=1;i<=n;i++) a[i]=read(); sort(a
+1,a+1+n); int pos=(n+1)/2,ans=0; if (x<a[pos]) { for (int i=1;i<=pos;i++) if (a[i]>x) ans+=a[i]-x; } else { for (int i=pos;i<=n;i++) if (a[i]<x) ans+=x-a[i]; } writeln(ans); return 0; }

CF 1037E Reach Median

題目描述

There are nn persons who initially don‘t know each other. On each morning, two of them, who were not friends before, become friends.

We want to plan a trip for every evening of mm days. On each trip, you have to select a group of people that will go on the trip. For every person, one of the following should hold:

  • Either this person does not go on the trip,
  • Or at least kk of his friends also go on the trip.

Note that the friendship is not transitive. That is, if aa and bb are friends and bb and cc are friends, it does not necessarily imply that aa and cc are friends.

For each day, find the maximum number of people that can go on the trip on that day.

輸入輸出格式

輸入格式:

The first line contains three integers nn , mm , and kk ( 2 \leq n \leq 2 \cdot 10^5, 1 \leq m \leq 2 \cdot 10^52n2105,1m2105 , 1 \le k < n1k<n ) — the number of people, the number of days and the number of friends each person on the trip should have in the group.

The ii -th ( 1 \leq i \leq m1im ) of the next mm lines contains two integers xx and yy ( 1\leq x, y\leq n1x,yn , x\ne yxy ), meaning that persons xx and yy become friends on the morning of day ii . It is guaranteed that xx and yy were not friends before.

輸出格式:

Print exactly mm lines, where the ii -th of them ( 1\leq i\leq m1im ) contains the maximum number of people that can go on the trip on the evening of the day ii .

輸入輸出樣例

輸入樣例#1: 復制
4 4 2
2 3
1 2
1 3
1 4
輸出樣例#1: 復制
0
0
3
3
輸入樣例#2: 復制
5 8 2
2 1
4 2
5 4
5 2
4 3
5 1
4 1
3 2
輸出樣例#2: 復制
0
0
0
3
3
4
4
5
輸入樣例#3: 復制
5 7 2
1 5
3 2
2 5
3 4
1 2
5 3
1 3
輸出樣例#3: 復制
0
0
0
0
3
4
4

說明

In the first example,

  • 1,2,31,2,3 can go on day 33 and 44 .

In the second example,

  • 2,4,52,4,5 can go on day 44 and 55 .
  • 1,2,4,51,2,4,5 can go on day 66 and 77 .
  • 1,2,3,4,51,2,3,4,5 can go on day 88 .

In the third example,

  • 1,2,51,2,5 can go on day 55 .
  • 1,2,3,51,2,3,5 can go on day 66 and 77 .

一共有n個人,他們開始互不認識,而每天早上不認識的兩個人會變成朋友。一共有m天,每天晚上有的人要去旅行,去旅行的人必須滿足ta有至少k個朋友也去旅行

求每天去旅行的最大人數

輸入格式:

第一行3個整數,表示n,m,kn,m,k

往下m行,每行兩個整數x,yx,y,表示這天早上xx和yy會變成朋友

輸出格式:

mm行,每行一個整數,表示每天晚上最多能去旅遊的人數

solution:

考慮離線做首先維護一個du,即圖上節點的入度如果入度小於k那麽就刪除這個節點然後維護刪去這個點以後發生的事情(比如其他邊被刪去,其他點又被刪去)

這樣維護就是一邊bfs就行(dfs也行)

具體來說就是先建好圖吧最終的狀態O(n)統計一遍然後,依次刪邊,刪邊的時候註意將du[u]--,du[v]--,如果du[u]或者du[v]等於0那麽把這個點刪去然後維護全局的答案

然後由於這個點刪去了,那麽和這個點有關系的所有邊都得刪去了,然後又造成一部分點的 du 減少於是又得維護這些點的狀態刪去或者不刪。。這就要bfs了。。。

註意刪過的點和邊不用重復刪,就是弄兩個數組看看對應的點和邊還在不在就行了。

Code:

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int MAXN=2e5+10;
struct Edge{ int u,v;};
struct A{ int node,id;};
vector<A>E[MAXN];
vector<Edge>R;
int ret,n,m,k,du[MAXN];
bool ok[MAXN],able[MAXN];
inline int read()
{
    int X=0,w=0; char c=0;
    while (!(c>=0&&c<=9)) w|=c==-,c=getchar();
    while (c>=0&&c<=9) X=(X<<1)+(X<<3)+(c^48),c=getchar();
    return w?-X:X;
}
void del(int u)
{
    if (ok[u]==false) return;
    ret--; ok[u]=false; du[u]=0;
    queue<int>q; q.push(u);
    while (!q.empty()) {
        int u=q.front();q.pop();
        for (int i=0;i<E[u].size();i++) {
            int v=E[u][i].node;
            if (ok[v]==false||able[E[u][i].id]==false) continue;
            du[v]--; 
            able[E[u][i].id]=false;
            if (du[v]<k) ok[v]=false,ret--,q.push(v); 
        }
    }
} 
void erase(int id)
{
    if (able[id]==false) return;
    int x=R[id].u,y=R[id].v;
    able[id]=false;
    du[x]--;if (du[x]<k&&ok[x]) del(x);
    du[y]--;if (du[y]<k&&ok[y]) del(y); 
}
signed main()
{
    n=read();m=read();k=read();
    for (int i=1;i<=m;i++) {
        int u=read(),v=read();
        E[u].push_back((A){v,i-1});
        E[v].push_back((A){u,i-1});
        du[u]++; du[v]++;
        R.push_back((Edge){u,v});
    }
    ret=n;
    memset(ok,true,sizeof(ok));
    memset(able,true,sizeof(able));
    for (int i=1;i<=n;i++) if (du[i]<k) del(i);
    stack<int>Ans;    
    for (int w=R.size()-1;w>=0;w--) {
        Ans.push(ret);
        erase(w);
    }
    while (!Ans.empty()) printf("%lld\n",Ans.top()),Ans.pop();
    return 0;
}

運氣大戰

你的班上nn個同學要去參加一項集體比賽。每個人有實力值和運氣值。每個人的實力值是確定的,但是運氣值是飄忽不定的。一個人的發揮是他的實力值wiwi 和運氣值的乘積,即wi⋅rciwi⋅rci。班級的發揮是所有人發揮之和。每個人有一個初始運氣值riri,但是每次比賽的時候,每個人的運氣值是所有人運氣值的一個排列,並且要滿足,排列之後ii的運氣值不是riri。即滿足,ii的運氣值是rcirci,{ci}{ci}是1−n1−n的排列,且滿足ci≠ici≠i。

現在有QQ場比賽,每場比賽前會交換兩人的初始運氣值。問你每場比賽班級可以獲得的最大發揮是多少。

輸入格式

第一行兩個整數 nn 和 QQ。

第二行nn個整數w1,...,wnw1,...,wn。

第三行nn個整數r1,...,rnr1,...,rn。

接下來QQ行,每行兩個整數ai,biai,bi表示每次要交換運氣值的兩個人的編號。

輸出格式

QQ行,每行一個答案,表示這場比賽班級的最大發揮。

樣例1

輸入

5 5
25 1 16 26 19 
11 27 4 8 20 
1 5
1 5
2 5
1 2
5 4

輸出

1527
1543
1543
1536
1536

樣例2

輸入

技術分享圖片
50 50
1913 135 637 739 1617 550 1918 688 1022 111 182 1127 1603 1315 36 1530 988 921 804 1856 767 747 556 413 588 395 1193 425 1205 168 453 1671 1813 251 1916 666 971 1651 1772 1877 792 63 980 740 1097 1523 765 1069 699 177
666 130 1955 1869 1271 1827 1762 833 1830 1521 1751 16 1260 907 792 1524 1732 780 122 1083 1651 1702 11 384 1664 1921 429 1388 615 1325 1988 15 1096 1607 1181 330 755 865 257 1959 1152 365 1826 851 355 359 1745 989 1179 877
43 44
10 32
18 38
19 44
17 27
12 35
38 49
6 49
6 44
26 42
44 7
8 29
45 36
25 4
39 4
26 44
40 37
8 28
17 19
39 11
13 15
14 34
47 3
44 6
40 48
26 37
4 12
40 9
40 16
6 34
38 39
31 13
19 41
8 3
7 20
39 47
2 25
12 38
9 47
35 28
46 21
9 26
26 10
28 23
8 33
10 37
23 13
48 49
19 8
10 24
View Code

輸出

技術分享圖片
68387076
68387076
68387076
68387076
68387076
68387076
68387076
68387076
68387076
68387076
68387076
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68387274
68386602
68386602
68386602
68386602
68386602
68386602
68386414
68386414
68386414
68386414
68386414
68386414
68386414
68386414
68386414
68386414
68387086
68387086
View Code

數據範圍

20% n10,q10n≤10,q≤10

另30% n1000,q100n≤1000,q≤100

另20% n30000,q500n≤30000,q≤500

100% 2n30000,1q10000,1wi,ri1e6,1aibin

本題時限:5s

由於排序不等式,我們盡量想順序放。兩邊都排序。

由於 n 個不能配的幹擾,又不能完全順序放。

有個結論,最後匹配出第 i 個人的運氣值是第 j 個的話,|i-j|\le2ij2。這個結論從最小化逆序對的個數來看,自己把附近幾個線連起來畫一畫證明一下。

這樣就可以用 dp[i]表示到 i 為止所有配好的最優答案。計算的時候需要用到前三輪的答案然後討論一下。這個是 O(nq)的,可以過70%。

用線段樹記錄區間答案。區間記錄這樣的信息:把這個區間前0-2個和後0-2個元素去掉的答案,用3x3的矩陣維護。這樣復雜度是O(qlogn)。

#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;

#define A first
#define B second
typedef long long ll;

int n,q;
vector<pair<int,int> > L, R;
int whereL[30013], whereR[30013];
ll cost[30013][2];
ll dp[30013];

const ll INF = -3.1e16;

inline ll match(int a, int b) {
    if (a<=0 || b<=0 || a>n || b>n) return INF;
    return (ll) L[a].A*R[b].A;
}

inline void update(int i) {
    if (i<=0 || i>n) return;
    cost[i][0] = match(i,i+1)+match(i+1,i);
    cost[i][1] = max(match(i,i+1)+match(i+1,i+2)+match(i+2,i),match(i,i+2)+match(i+1,i)+match(i+2,i+1));
}

int main() {
    scanf("%d%d",&n,&q);
    for (int i=0;i<=n;i++) {
        L.push_back(make_pair(0,i));
        R.push_back(make_pair(0,i));
    }
    for (int i=1;i<=n;i++) scanf("%d",&L[i].A);
    for (int i=1;i<=n;i++) scanf("%d",&R[i].A);
    sort(L.begin(),L.end());
    sort(R.begin(),R.end());
    for (int i=1;i<=n;i++) {
        whereL[L[i].B] = i;
        whereR[R[i].B] = i;
    }
    for (int i=1;i<=n;i++) update(i);
    for (int Q=0;Q<q;Q++) {
        int a,b;
        scanf("%d%d",&a,&b);
        swap(R[whereR[a]].B,R[whereR[b]].B);
        swap(whereR[a],whereR[b]);
        for (int i=-2;i<=0;i++) {
            update(whereL[a]+i);
            update(whereR[a]+i);
            update(whereL[b]+i);
            update(whereR[b]+i);
        }
        dp[n+1] = 0;
        for (int i=n;i;i--) {
            dp[i] = max(dp[i+2]+cost[i][0],dp[i+3]+cost[i][1]);
            if (L[i].B!=R[i].B) dp[i] = max(dp[i],(ll) L[i].A*R[i].A+dp[i+1]);
        }
        printf("%lld\n",dp[1]);
    }

    return 0;
}

NOI.AC 20181103 題解