1. 程式人生 > >2018ICPC南京網絡賽

2018ICPC南京網絡賽

路徑 三個點 game ron 開始 mps mes n+1 避免

2018ICPC南京網絡賽

A. An Olympian Math Problem

題目描述:求\(\sum_{i=1}^{n} i\times i! \%n\)

solution
\[(n-1) \times (n-1)! \% n= (n-2)!(n^2-2n+1) \%n =(n-2)!\]
\[(n-2+1)\times (n-2)! \% n= (n-3)!(n^2-3n+2) \%n =(n-3)! \times 2\]
以此類推,最終只剩下\(n-1\)

時間復雜度:\(O(1)\)

B. The writing on the wall

題目描述:有一個\(n\times m\)

的網格圖,其中有些格子是黑色,其它都是白色,問不含黑色格子的矩形有多少個。

solution
先預處理出每個格子向右延伸的最長距離是多少,記作\(ri[i][j]\)
對於每一列\(j\),將行按\(ri\)從大到小排序,按順序插回去,用並查集維護連通塊,當將第\(i\)行插回去是,\(i\)所在的連通塊最長延伸距離為\(ri[i][j]\),為避免重復,只算跨過\(i\)這一行的貢獻。

時間復雜度:\(O(n^3)\)

C. GDY

據說是比較坑的模擬題,隊友做的。

D. Jerome‘s House

題目描述:在一個凸包內放三個半徑為\(r\)的圓(圓可以相交),求圓心所形成的三角形的面積的最大值的兩倍。

solution
將凸包的所有邊向內移動\(r\),用半平面交求出之後的凸包,然後在凸包上求面積最大的三角形,枚舉兩個點,然後第三個點是單調的。

時間復雜度:\(O(n^2)\)

E. AC Challenge

裸狀壓\(dp\).

F. An Easy Problem On The Trees

G. Lpl and Energy-saving Lamps

裸線段樹。

H. Set

題目描述:有\(n\)個點, 每個點有一個權值。\(m\)個操作,操作有三種:

  1. 給定\(u, v\),如果\(u, v\)不在同一個集合,那麽將它們所在的集合合並
  2. 給定\(u\),將\(u\)所在集合的點的權值加一。
  3. 給定\(u, k, x\),求\(u\)所在集合的點的權值模\(2^k\)等於\(x\)的有多少。

solution
看到第三個操作,就會想到用\(trie\)來維護每個集合的數(越接近樹根數位越低),操作一就是將兩棵\(trie\)暴力合並,由於只有合並操作,因此每個點只會被合並一次,因此總的時間復雜度還是\(O(nlogn)\),對於操作二,可以在\(trie\)上面打標記,表示子樹要加數,假設加的數為\(x\),若\(x\)為奇數,則本來該數位是\(0\)的數變成\(1\)\(1\)變成\(0\),並且要進位,即交換兒子,交換後兒子\(0\)的標記加一;若\(x\)為偶數,則直接把標記往下打即可,往下打得標記為\(x/2\).

時間復雜度:\(O((n+m)k)\)

#include <bits/stdc++.h>
using namespace std;

const int maxn=int(6e5)+100;

struct node
{
    node *son[2];
    int cnt;
    int mark;

    node()
    {
        cnt=mark=0;
        son[0]=son[1]=NULL;
    }

    void down()
    {
        if (mark & 1) swap(son[0], son[1]);
        if (son[0]) son[0]->mark+=(mark>>1)+(mark & 1);
        if (son[1]) son[1]->mark+=(mark>>1);
        mark=0;
    }
};

node memory[maxn*31];
node *mem=memory;
int n, m;
int dsu[maxn];
node *trie[maxn];

void insert(node *cur, int v)
{
    ++cur->cnt;
    for (int i=0; i<30; ++i, v>>=1)
    {
        if (!cur->son[v & 1]) cur->son[v & 1]=mem++;
        cur=cur->son[v & 1];
        ++cur->cnt;
    }
}
void read()
{
    scanf("%d%d", &n, &m);
    for (int i=1; i<=n; ++i) dsu[i]=-1;
    for (int i=1; i<=n; ++i)
    {
        int v;
        scanf("%d", &v);
        trie[i]=mem++;
        insert(trie[i], v);
    }
}
int dsu_find(int cur)
{
    return (dsu[cur]<0? cur:(dsu[cur]=dsu_find(dsu[cur])));
}
void dsu_merge(int &x, int &y)
{
    if (dsu[x]>dsu[y]) swap(x, y);
    dsu[x]+=dsu[y];
    dsu[y]=x;
}
node *merge(node *x, node *y)
{
    if (!x) return y;
    if (!y) return x;
    if (x->mark) x->down();
    if (y->mark) y->down();
    x->cnt+=y->cnt;
    x->son[0]=merge(x->son[0], y->son[0]);
    x->son[1]=merge(x->son[1], y->son[1]);
    return x;
}
int ask(node *cur, int k, int x)
{
    if (cur->mark) cur->down();
    for (int i=0; i<k; ++i, x>>=1)
    {
        if (!cur->son[x & 1]) return 0;
        cur=cur->son[x & 1];
        if (cur->mark) cur->down();
    }
    return cur->cnt;
}
void solve()
{
    for (int i=1; i<=m; ++i)
    {
        int type, u, v, x;
        scanf("%d%d", &type, &u);
        u=dsu_find(u);
        switch (type)
        {
            case 1:
                scanf("%d", &v);
                v=dsu_find(v);
                if (u==v) break;
                dsu_merge(u, v);
                trie[u]=merge(trie[u], trie[v]);
                break;
            case 2:
                ++trie[u]->mark;
                break;
            case 3:
                scanf("%d%d", &v, &x);
                printf("%d\n", ask(trie[u], v, x));
                break;
        }
    }
}
int main()
{
    read();
    solve();
    return 0;
}

I. Skr

題目描述:給定一個只含數字的字符串,求不同回文子串的和(子串所代表的數字的和)

solution
裸回文樹。

J. Sum

題目描述:定義\(f(n)\)\(n\)分解為兩個非平方數乘積的方案數(有序數對),非平方數指的是沒有平方數因子的數,\(1\)除外。問\(\sum_{i=1}^{n} f(i)\)

solution
將一個數\(n\)分解質因數,若某個質因子的冪大於\(2\),則\(f(n)=0\),因為無論怎麽分,總有一個數會有兩個該質因子;否則\(f(n)=2^{冪次為1的質因子的個數}\)。而這東西顯然是積性函數,因此可以線性篩預處理。

時間復雜度:\(O(n)\)

K. The Great Nim Game

題目描述:有\(n\)堆石子,每堆石子的個數為\(f(n)\),一開始可以移走若幹堆石子,使得在剩下的石子堆中玩\(Nim\)遊戲先手必勝,問移走石子堆的方案數。

solution
如果直接給出每堆石子的個數,那這題就很簡單了,就是算使得剩下的數異或值不為\(0\)即可,這個可以用狀壓\(dp\)。但這道題的\(n\)很大,\(f(n)\)是由遞推式給出的。觀察式子可知\(f(n)\)有一個長度為\(k\)的循環,因此可以算出每種數字有多少個,只是要用高精度表示而已,每種數字選奇數個或偶數個,因此方案數是\(2^{個數-1}\),因為個數很大,所以要用歐拉定理降冪。

時間復雜度:\(O(n+2^kk)\)

L. Magical Girl Haze

題目描述:有一個\(n\)個點\(m\)條邊的圖,現在可以將不多於\(k\)條邊的權值變成\(0\),求\(1\)\(n\)的最短路徑。

solution
拆點,將一個點拆成\(k+1\)個,表示已經變了多少條邊。然後用\(dijkstra\)才能過。(好像\(SPFA+SLF\)優化也可以)

時間復雜度:\(O(mlog(nk))\)

2018ICPC南京網絡賽