2018ICPC南京网络赛

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))\)

转载于:https://www.cnblogs.com/GerynOhenz/p/9595316.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值