2019.11.7解題報告

\[2019.11.7解題報告\]


node

T1:同花順

題目描述

所謂同花順,就是指一些撲克牌,它們花色相同,而且數字連續。
如今我手裏有 \(n\)張撲克牌,但它們可能並不能湊成同花順。我如今想知道,最
少更換其中的多少張牌,我能讓這 \(n\) 張牌湊成一個同花順?ios

輸入格式

第一行一個整數\(n\),表示撲克牌的張數。
接下來 \(n\) 行,每行兩個整數 \(a_i\)\(b_i\) 。其中 \(a_i\) 表示第 \(i\) 張牌的花色,\(b_i\) 表示第
\(i\) 張牌的數字。
(注意: 這裏的牌上的數字不像真實的撲克牌同樣是 \(1\)\(13\), 具體見數據範圍)git

輸出格式

一行一個整數,表示最少更換多少張牌能夠達到目標。函數

樣例輸入 1
5
1 1
1 2
1 3
1 4
1 5
樣例輸出 1
0
樣例輸入 2
5
1 9
1 10
2 11
2 12
2 13
樣例輸出 2
2
數據範圍

對於 \(30\%\) 的數據,\(n ≤ 10\)
對於 \(60\%\) 的數據,\(n ≤ 10^5\)\(1 ≤ a_i ≤ 10^5\)\(1 ≤ b_i ≤ n\)
對於 \(100\%\)的數據,\(n ≤ 10^5 ,1 ≤ a_i ,b_i ≤ 10\)測試

Solution:

去重+離散化
貪心的選擇花色最多的順子,而後枚舉每一個數字做爲左端點在這個花色裏能組成長度爲\(n\)的順子須要修改或者添加幾張牌,記錄答案便可。spa

注意:
\(1\).\(vector\)的去重方法:
先排序再使用\(unique\)函數3d

\(unique\)函數將重複的元素放到\(vector\)的尾部,而後返回指向第一個重複元素的迭代器,再用\(erase\)函數將從這個元素到最後的全部元素清除code

Code:
#include <vector>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5+7;
int n, ans, a[N], b[N], r;
vector <int> der, num[N];
int read() {
    int s = 0, w = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-') w = -1; ch = getchar();}
    while(isdigit(ch)) {s = s * 10 + ch - '0'; ch = getchar();}
    return s * w;
}
int main() {
    n = read();
    for(int i = 1; i <= n; i++) a[i] = read(), b[i] = read(), der.push_back(a[i]);
    sort(der.begin(), der.end());
    der.erase(unique(der.begin(), der.end()), der.end());
    for(int i = 1; i <= n; i++) a[i] = lower_bound(der.begin(), der.end(), a[i]) - der.begin() + 1;
    for(int i = 1; i <= n; i++) num[a[i]].push_back(b[i]);
    for(int i = 1; i <= der.size(); i++) {
        sort(num[i].begin(), num[i].end());
        num[i].erase(unique(num[i].begin(), num[i].end()), num[i].end());
        for(int j = 0; j < num[i].size(); j++)
            r = lower_bound(num[i].begin(), num[i].end(), num[i][j] + n - 1)  - num[i].begin() - 1, ans = max(ans, r - j + 1);
    }
    cout << n - ans << endl;
    return 0;
}

話說\(STL\)真的挺好用的感受blog

T2:作實驗

題目描述

有一天,你實驗室的老闆給你佈置的這樣一個實驗。
首先他拿出了兩個長度爲\(n\) 的數列 \(a\)\(b\),其中每一個 \(a_i\) 以二進制表示一個集
合。例如數字 \(5 = (101)_2\) 表示集合 \({1,3}\)。第 \(i\) 次實驗會準備一個小盒子,裏面裝
着集合 \(a_i\) 全部非空子集的紙條。老闆要求你從中摸出一張紙條,若是知足你摸出的
紙條是 \(a_i\) 的子集而不是 $a_{i−b_i} \(,\)a_{i−b_i +1}$ ,...,\(a_{i−1}\) 任意一個的子集,那麼你就要
反之,你就逃過一劫。
令你和老闆都沒有想到的是,你居然每次都逃過一劫。在慶幸之餘,爲了知道
這件事發生的機率,你想要算出每次實驗有多少紙條能使你
排序

輸入格式

第一行一個數字 \(n\)
接下來 \(n\) 行,每行兩個整數,分別表示 \(a_i\)\(b_i\)

輸出格式

\(n\) 行,每行一個數字,表示第 \(i\) 次實驗能使你 *** 的紙條數。

樣例輸入 1
3
7 0
15 1
3 1
樣例輸出 1
7
8
0
4
數據範圍

對於 \(30\%\) 的數據,\(n,a_i ,b_i ≤ 100\)
對於 \(70\%\) 的數據,\(n,a_i ,b_i ≤ 60000\)
對於 \(100\%\) 的數據,\(n,a_i ,b_i ≤ 10^5\)
保證全部的 \(a_i\) 不重複,\(b_i < i\)

Solution:

枚舉每個\(a[i]\)的子集
\(f[s]\)\(s\)這個子集最後一次出如今哪一個\(a[i]\)當中
\(a[1]\)\(a[n]\)依次處理。對\(a[i]\),枚舉其全部的子集\(s\),若\(f[s]>=i-b[i]\),則答案\(+1\)。同時更新\(f[s]\)

Code:

真的懶了,第一題就看了好久太浪費時間了
\(so!std:\)

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int n,m,f[N];
int scan(){int i=0;scanf("%d",&i);return i;}
void print(int x,int t=7){
    if(!t)return;
    print(x/2,t-1);
    printf("%d",x&1);
}
int main(){
    int i,j,k,a,b;
    freopen("test.in","r",stdin);
    freopen("test.out","w",stdout);
    n = scan();
    int t,ans;
    for(i=1;i<=n;i++){
        a = scan();b = scan();
        t = a;
        ans =0;
        do{
            if(f[t] < i-b)ans++;
            f[t] = i;
            //print(t);printf("\n");
            t = a&(t-1);
        }while(t);
        printf("%d\n",ans);
    }
    return 0;
}

T3:拯救世界:

題目描述

\(C\) 城全部的道路都是單向的。不一樣道路之間有路口,每一個路口都有一個大樓。
有一天, 城市裏的全部大樓由於不明緣由, 忽然着火了。 做爲超人的你要去拯救
這些大樓。初始的時候你在 \(S\) 號樓,最後你必須到達某個有補給站的大樓,你能夠
沿着單向道路行駛。你能夠通過某條道路或者某個大樓若干次,通過一個大樓你就
能夠消滅一個大樓的大火。每一個大樓都有一個重要程度,最後這個任務的評價分數
就是你通過的全部大樓的重要度之和(若重複通過某個大樓屢次,則不重複算分) 。
你是一個聰明的超人,你想知道,經過合理的規劃路線,你此次任務能獲得的
最高得分是多少。
注意,該城市的道路可能有重邊或自環。

輸入格式

第一行包括兩個整數 \(n\)\(m\)\(n\) 表示路口的個數(即大樓的個數) ,\(m\) 表示道路
的條數。
接下來 \(m\) 行,每行兩個整數 \(x,y\),表示 \(x\)\(y\) 之間有一條單向道路。
接下來 \(n\) 行,每行一個整數,按順序表示每一個大樓的重要度。
接下來一行包含兩個整數 \(S\)\(P\)\(S\) 是出發的路口(大樓)的編號,\(P\) 是有補
給站的大樓的數量。
接下來一行 \(P\) 個整數,表示有補給站的大樓的編號。

輸出格式

輸出一行一個整數,表示你得分的最大值。

樣例輸入 1
6 7
1 2
2 3
3 5
6
2 4
4 1
2 6
6 5
10
12
8
16
1
5
1 4
4 3 5 6
樣例輸出 1
47
數據範圍

對於 \(一、二、3\) 測試點,\(N,M ≤ 300\)
對於 \(四、五、六、七、八、九、10\) 測試點,\(N,M ≤ 3000\)
對於 \(十一、十二、1三、1四、15\) 測試點,\(N,M ≤ 500000\)。每一個大樓的重要度均爲非
負數且不超過 \(4000\)
輸入數據保證你能夠從起點沿着單向道路到達其中的至少一個有補給站的大
樓。
注意,輸入數據中存在樹和鏈的特殊狀況

Solution:

\(Tarjan\)縮點+拓撲排序找最長路

考場\(Code\)
//tarjan縮點+dfs
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 5e5+7;
int head[N], cnt, dfn[N], low[N], tot, num, belon[N], siz[N], w[N], stac[N], top, st, p;
int Head[N], n, m;
bool vis[N], ok[N], okk[N], flag;
long long ans, sum;
struct node {int nxt, to;}e[N];
struct Node {int nxt, to;}E[N];
int read() {
    int s = 0, w = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-') w = -1; ch = getchar();}
    while(isdigit(ch)) {s = s * 10 + ch - '0'; ch = getchar();}
    return s * w;
}
void add(int x, int y) {
    E[++cnt].nxt = Head[x];
    E[cnt].to = y;
    Head[x] = cnt;
}
void tarjan(int u) {
    int v;
    dfn[u] = low[u] = ++tot;
    stac[++top] = u, vis[u] = 1;
    for(int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if(!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
        else if(vis[v]) low[u] = min(low[u], dfn[v]);
    }
    if(dfn[u] == low[u]) {
        num++;
        do{
            v = stac[top--], vis[v] = 0;
            belon[v] = num, siz[num] += w[v];
            if(ok[v]) okk[num] = 1;
        }while(v != u);
    }
}
void dfs(int x) {
    if(okk[x]) ans = max(ans, sum);
    for(int i = Head[x]; i; i = E[i].nxt)
        sum += siz[E[i].to], dfs(E[i].to), sum -= siz[E[i].to];
}
int main() {
    freopen("save.in", "r", stdin); freopen("save.out", "w", stdout);
    n = read(), m = read();
    for(int i = 1, x, y; i <= m; i++) x = read(), y = read(), e[i].nxt = head[x], e[i].to = y, head[x] = i; 
    for(int i = 1; i <= n; i++) w[i] = read();
    st = read(), p = read();
    for(int i = 1, x; i <= p; i++) x = read(), ok[x] = 1;
    for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
    for(int i = 1; i <= n; i++)     
        for(int j = head[i]; j; j = e[j].nxt)
            if(belon[i] != belon[e[j].to]) add(belon[i], belon[e[j].to]);
    sum += siz[belon[st]], dfs(belon[st]);
    cout << ans << endl;
    fclose(stdin); fclose(stdout);
    return 0;
}

\(std\)

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

const int MAXN = 500100;

int father[MAXN] = {0};
int dfsTime[MAXN] = {0}, linkTime[MAXN] = {0}, cct[MAXN] = {0}, st[MAXN] = {0};
bool statue[MAXN] = {0};
int nowTime = 0, cnt = 0, top = 0;
int point[MAXN] = {0}, nxt[MAXN] = {0}, v[MAXN] = {0}, cur[MAXN] = {0}, tot = 0;
int money[MAXN] = {0};
bool cctbar[MAXN] = {0}, bar[MAXN] = {0};
int cctmoney[MAXN] = {0}, f[MAXN] = {0};
int x[MAXN], y[MAXN], n, m, start, p;
int rudu[MAXN] = {0}, able[MAXN] = {0};

inline void memorycheck()
{
    int memory = sizeof(dfsTime) + sizeof(linkTime) + sizeof(cct) + sizeof(st) + sizeof(point)
               + sizeof(nxt) + sizeof(v) + sizeof(money) + sizeof(cctbar) + sizeof(bar)
               + sizeof(cctmoney) + sizeof(f) + sizeof(x) + sizeof(y);
    printf("%d\n", memory / 1024 / 1024);
}

inline void clear()
{
    tot = 0;
    memset(point, 0, sizeof(point));
    memset(nxt, 0, sizeof(nxt));
}

inline void addedge(int x, int y)
{
    tot++;
    nxt[tot] = point[x];
    point[x] = tot;
    v[tot] = y;
}

inline void dfs(int now)
{
    while (now)
    {
        if (!dfsTime[now])
        {
            dfsTime[now] = linkTime[now] = ++nowTime;
            st[++top] = now;
            cur[now] = point[now];
        }
        else
        {
            if (statue[now])
            {
                linkTime[now] = min(linkTime[now], linkTime[v[cur[now]]]);
                statue[now] =false;
            }
            cur[now] = nxt[cur[now]];
        }
        
        if (!cur[now])
        {
            if (dfsTime[now] == linkTime[now])
            {
                cnt++;
                while (1)
                {
                    cct[st[top--]] = cnt;
                    if (st[top + 1] == now) break;
                }
            }
            now = father[now];
            continue;
        }
        
        if (!dfsTime[v[cur[now]]])
        {
            statue[now] = true;
            father[v[cur[now]]] = now;
            now = v[cur[now]];
            continue;
        }
        else
            if (!cct[v[cur[now]]])
                linkTime[now] = min(linkTime[now], dfsTime[v[cur[now]]]);
    }
}

int main()
{
    freopen("save.in", "r", stdin);
    freopen("save.out", "w", stdout);
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++)
    {
        scanf("%d%d", &x[i], &y[i]);
        addedge(x[i], y[i]);
    }
    for (int i = 1; i <= n; i++) scanf("%d", &money[i]);
    scanf("%d%d", &start, &p);
    for (int i = 1; i <= p; i++)
    {
        int x;
        scanf("%d", &x);
        bar[x] = true;
    }
    for (int i = 1; i <= n; i++)
        if (!dfsTime[i])
            dfs(i);
    clear();
    for (int i = 1; i <= n; i++)
    {
        cctbar[cct[i]] |= bar[i];
        cctmoney[cct[i]] += money[i];
    }
    for (int i = 1; i <= m; i++)
        if (cct[x[i]] != cct[y[i]])
        {    
            addedge(cct[x[i]], cct[y[i]]);
            rudu[cct[y[i]]]++;
        }
    queue<int> q;
    while (!q.empty()) q.pop();
    for (int i = 1; i <= cnt; i++)
        if (!rudu[i])
            q.push(i);
    able[cct[start]] = 1;
    int ans = 0;
    while (!q.empty())
    {
        int now = q.front(); q.pop();
        f[now] += cctmoney[now] * able[now];
        if (cctbar[now])
            ans = max(ans, f[now]);
        for (int temp = point[now]; temp; temp = nxt[temp])
        {
            rudu[v[temp]]--;
            if (!rudu[v[temp]]) q.push(v[temp]);
            f[v[temp]] = max(f[v[temp]], f[now]);
            able[v[temp]] |= able[now];
        }
    }
    printf("%d\n", ans);
}

之後不想寫解題報告了太浪費時間了\(wxf\)會說個人!

謝謝收看, 祝身體健康!

相關文章
相關標籤/搜索