2022国防科技大学程序设计竞赛预选赛题解

本场题目难度 && 建议做题顺序:ED GNBFH IJLCM AK

代码在最后。每道题目标题后的括号代表前置知识。

E:This is an easy problem

纯签到题,直接输出答案即可。

D:Limit

简单的高数题,可以用泰勒展开 or 洛必达。
不要忘了a!=0

G:Fibonacci (线性dp)

要求的是十位数,那么只需要记录每个数%100的结果就好,查询的时候输出结果/10。

N:

将图形分为以下的区域:
2022国防科技大学程序设计竞赛预选赛题解_第1张图片
在二维字符数组中全初始化为空格,在平行四边形区域内修改空格为1即可。
其输入的大小n用来计算平行四边形的长和高,长均为 n ,高对于最左边的平行四边形为 3 4 n \frac{3}{4}n 43n,三个平行的较大的平行四边形为 5 4 n \frac{5}{4} n 45n ,最后小的为 n 4 \frac{n}{4} 4n

B: 银河杂货铺与月湖大市场采购

我们可以将这个环直接从月湖大市场门口剪开,实际上问题可以化成一个序列,我有一系列询问,对于每个询问我可以在序列上或顺序或逆序查找询问的值,统计每次查找中逆序或顺序查找时比对次数中比较大的那个次数的和。

相对简单的序列预处理题目。

我们注意此时序列中和询问的值的大小都不超过 100000 100000 100000 ,我们可以对于顺序和逆序分别用 100000 100000 100000 个桶来记录,对于每个值来说,顺序查找时第一个这个值是在哪个位置,对于逆序亦然。这样我们得到询问的值时,就可以直接比对对于询问的值顺序和逆序的桶哪个桶中位置离两端距离更大,就统计哪个答案即可。

F: 五四银河展和文案编辑

对于每个操作 1 1 1 ,我们记录一个数组,数组分别代表 a b c d . . . y z A B C D . . . X Y Z abcd...yzABCD...XYZ abcd...yzABCD...XYZ 中第 i i i 个字符最终变成了哪个字符,数组初始化为每个字符自己。每次操作 1 1 1 ,我不修改原字符串,只修改数组,对于数组中的各个值,把数组中每个 c 1 c_1 c1 的位置设为 c 2 c_2 c2。对于每个操作 2 2 2,我直接将数组清空,将原字符串用新字符串覆盖。操作结束后,将字符串中每个字符按照数组记录的值修改即可。时间复杂度 O ( 52 m + 100 n ) O(52m + 100n) O(52m+100n)

H:薛定谔的猫与量子纠缠 (DFS)

我们将每个实验装置看做一个节点,每个量子纠缠关系看做连接两节点的边,于是该题可看做一个图。

然后我们通过DFS,从已经有0/1状态的节点开始,给相邻节点标上不同于上一个节点的状态即可,最后按1~n的顺序输出即可。

I:宣传海报组与海报制作

一道比较显然但不太好证明的贪心。

问题简述:将 n n n 个数分组,每组仅可有 1 1 1 个或 2 2 2 个,如何分组使得其每个组中数和的最大值与最小值的差最小。
我们枚举有多少组含2个,记为 i i i 。对于枚举的每个 i i i ,最优策略一定是:
在这 n n n 个数中,取前 n − 2 i n - 2i n2i 大的数单独成组,然后每次取剩下数中最大的和最小的数作为一组。
证明:
最初 n n n个数时,考虑选1个数的人,当前最优策略一定可以是选则最大的数,记为 a a a。证明:假设不选 a a a b b b,这个前提下的最优策略记为 F b F_b Fb. 可以构造一个策略 F a F_a Fa,与 F b F_b Fb的唯一不同是其中的 a a a, b b b交换位置。记某策略中组内和的最小值为min,最大值为max。容易得到 F b ( m i n ) ≤ F a ( m i n ) F_b(min) \le F_a(min) Fb(min)Fa(min) F b ( m a x ) ≥ F a ( m a x ) F_b(max) \ge F_a(max) Fb(max)Fa(max)。那么 F a F_a Fa一定比 F b F_b Fb优。
这样可以安排完所有选1个数的人。接下来对于剩下最大的数,一定和最小的数配对,证明方法和上面类似。

如有更好的解决方案可直接在群里给出QwQ或给出题人。

J: 天河开放日与参观路线设计 (Flyod)

多源最短路。

O ( n 3 ) O(n^3) O(n3)解法:魔改 F l o y d Floyd Floyd

对于以下 F l o y d Floyd Floyd

for (int k = 1; k <= n; ++k) 
    for (int i = 1; i <= n; ++i) 
        for (int j = 1; j <= n; ++j) 
        	mp[i][j] = max(mp[i][j] , mp[i][k] + mp[k][j]);

最外层枚举 k k k 的循环中的节点的顺序可以调整,我们将其调整为我们删点的顺序,也就是说,我们倒着考虑,我们将删点看做加点, k k k 层的循环中用于松弛的节点按照加点的顺序,每次加点完进行 i , j i, j i,j 层松弛操作,统计答案,记录该点已加入,在计算总和时记录已加入点得答案。
这样做是正确的,因为:
根据对 f l o y d floyd floyd 的理解我们可以明白,记集合 S S S 为已经作为最外层循环的点集,那么只含 S S S 内点的图中,最短路已经是正确的。
而为了保证复杂度的正确性,我们在内层循环时仍要更新除了 S S S 以外的点的最短路。

O ( n 3 l o g n ) O(n^3logn) O(n3logn)解法:直接暴力 D i j k s t r a + h e a p Dijkstra + heap Dijkstra+heap

直接将某些仍存在的点标记,每次对 n n n 个点各进行一次 D i j k s t r a Dijkstra Dijkstra,要用优化,否则 O ( n 4 ) O(n^4) O(n4) 无法通过。

推荐 O ( n 3 ) O(n^3) O(n3) 做法,帮助理解 Floyd。

L:银河讲堂与座位安排 (数据结构:线段树 or 并查集等)

直接建 m m m 个线段树,区间赋值成0,维护区间最大值(有没有1)就能过,复杂度 O ( ( q ∗ m + n ) l o g n ) O((q*m+n)logn) O((qm+n)logn)。(不过赛时好多TLE,应该是常数太大)

这里再介绍std做法:(区间and,维护区间or)

我们发现 m ≤ 30 m \le 30 m30 ,即不超过 31 31 31 ,考虑将每排压缩成一个 i n t int int 类型的值,第 i i i 列座位在此值中表现为二进制的第 i − 1 i - 1 i1 位数。我们最初设为 2 m − 1 2^m - 1 2m1,即各个数位上全为 1 1 1 。对于 n n n 个值,我们建一颗线段树。

每个修改也可压缩为一个 i n t int int 类型的值 y y y,对于每个不可坐人的列 j j j,将 y y y 的第 j − 1 j - 1 j1 位设为 0 0 0,其他为设为 1 1 1

修改即为对于区间每个数均 a n d and and y y y

对于每个询问,在线段树上统计区间 o r or or 的值,并转化为各个位置的值,并统计答案即可。

另外并查集也可做,复杂度是 O ( m ∗ n ) O(m*n) O(mn)。大致思路:每列一个并查集,区间赋值时,把区间的所有点指向 f a [ r + 1 ] fa[r+1] fa[r+1],查询时判断 f a [ l ] fa[l] fa[l]是否大于 r + 1 r+1 r+1,具体见这里

C:counting rhombus

对于菱形,入手点是对角线垂直平分。
可以把所有中点相同的对角线分成一类。 分类时,按中点(x,y)排序即可。
处理一类中的个数,方法有很多,一种通用的思路可以是:先按对角线的斜率大小排序,遍历一遍,对于当前斜率为 k k k , 答案+=斜率 − 1 k -\frac{1}{k} k1 的个数。这个过程可以用map维护 (注意尽量不要引入浮点数以免产生误差。斜率可用分数表示,注意斜率不存在,最简分数等问题)。
本题其实不用严格按斜率排序。一种实现方法如下:
考虑计入答案的一对斜率,分数表示为 x y \frac{x}{y} yx , − y x -\frac{y}{x} xy ( x , y ≥ 0 x,y \ge 0 x,y0)。如果按分子排序,一定有先遍历 − y -y y 再轮到 x x x. 遍历过程中用 map记录 x y \frac{x}{y} yx 的个数,遍历到 x y \frac{x}{y} yx 时加上 − y x -\frac{y}{x} xy的个数即可。这样做是正确的必须要保证 分母>=0 且 为最简分数。
时间复杂度O( n 2 l o g n n^2 log n n2logn)

M:Fluorescent night running (树形dp)

以给定的k作为根节点。设:
d p i dp_i dpi : 子树 i i i 中,以 i i i 为一条路径起点,两条路径之和的最大值。
d i d_i di:子树 i i i中,以 i i i 为起点最长的一条路径的长度。
f i f_i fi : 子树 i i i 中,最长的一条路径的长度。
则答案为 d p i dp_i dpi .
如何转移?
d i d_i di 最简单, d i d_i di = max( d s o n d_{son} dson+ length(i,son) ).
f i f_i fi 的转移有两种情况:最大的 f s o n f_{son} fson 或子树中最长的以 i i i 为起点的两条路径。
d p i dp_i dpi:考虑以 i i i 为起点的路径,其向下指向 s o n son son。分两种:
当另一条路径在 s o n son son 的子树中时: 将 d p s o n dp_{son} dpson + length(i,son) 转移给 d p i dp_i dpi;
当另一条路径不在 s o n son son 的子树中时:此时以 i i i 为起点的路径可以随意取最长,为 d s o n d_{son} dson。而另一条路径要么是在不为 s o n son son 的某个子树中,要么是除去 d s o n d_{son} dson 外的两条到 i i i 的最长路径拼接。
时间复杂度O( n n n)。
实现细节可参考代码。

K: game of number (素数筛,逆元,思维)

本题想起来其实不难,但细节多,码量大。
首先是如何求一个整数 x x x 的因子个数,即 f ( x ) f(x) f(x):
x x x 分解质因数: x = p 1 k 1 p 2 k 2 . . . p n k n x=p_1^{k_1} p_2^{k_2} ... p_n^{k_n} x=p1k1p2k2...pnkn ,其中 p 1 , p 2 , . . . , p n p_1,p_2,...,p_n p1,p2,...,pn为素数。则 f ( x ) = ( k 1 + 1 ) ( k 2 + 1 ) . . . ( k n + 1 ) . f(x)=(k_1+1)(k_2+1)...(k_n+1). f(x)=(k1+1)(k2+1)...(kn+1).
对于 [ 1 , 1 e 7 ] [1,1e7] [1,1e7]以内的质因子可以线性筛 O ( n ) O(n) O(n) 预处理。
x x x 分解质因数的方法:底数的话遍历 s q r t ( x ) sqrt(x) sqrt(x) 以内的质数,最后如果 x > 1 x>1 x>1则除剩下的 x x x 也为素数。指数最多和为 l o g log log级别,复杂度为 O ( s q r t ( x ) 以 内 的 质 数 + l o g ( x ) ) O(sqrt(x)以内的质数 + log(x)) O(sqrt(x)+log(x))

然后考虑一个重要的性质:若 y = k x y = k x y=kx,且 k k k 为素数,则 f ( x ) ≤ f ( y ) f(x) \le f(y) f(x)f(y)。证:从 x x x k k k y y y,分数结果中,分母乘 k k k,而分子乘 ( ( n u m [ k ] + 1 ) / n u m [ k ] ((num[k]+1)/num[k] ((num[k]+1)/num[k],最大为2 , 其 中 n u m [ k ] ,其中num[k] num[k] 表示 x x x k k k 为底数的指数。
首先线性筛,并得到初始 n n n 的所有素因子,复杂度 O ( 1 e 7 ) O(1e7) O(1e7)
首先Bob的最优操作一定可以是,先操作完所有乘法再操作除法。
考虑Alice不提前declare,等到所有卡片取完的情况:此时最终的数恒定,与取卡片的顺序无关,结果记为 a n s ans ans。可以先操作所有乘法的卡片,再操作除法,最终得到此数的各个素因子及其指数。此部分复杂度 O ( 446 ∗ q ) O(446*q) O(446q) ,446为 s q r t ( 1 e 7 ) sqrt(1e7) sqrt(1e7) 以内的素数个数。注意乘法是指数加,除法是指数减,且不能减到负数。
考虑提前declare结束游戏:易知一定是在第一轮declare。此时Bob要最小化 f ( n ) f(n) f(n),那么一定是操作一次乘法。枚举所有乘法操作得到对应结果,比较选择最小的结果即可,记为 r e s res res。此部分复杂度 O ( 446 ∗ q ) O(446*q) O(446q)。同时注意特判没有乘法卡片存在的情况。
Alice的最优策略是比较 r e s res res a n s ans ans 选择较大的那个即可。对于 r e s res res, 其分母最大为 1 e 14 ∗ 1 e 7 1e14 * 1e7 1e141e7级别,要用int128存。对于 a n s ans ans的分子分母,太大而int128 ( 1 e 38 级 别 ) (1e38级别) 1e38都远远存不下,怎么办? 可以注意到当 n n n f ( n ) 相 对 于 n 很 小 f(n)相对于n很小 f(n)n,此时 a n s ans ans一定小于 r e s res res. int128范围内一个整数的因子个数上限为 3 e 8 3e8 3e8(证明见代码最下方),那么分母大于 1 e 31 1e31 1e31时, 3 e 8 / 1 e 31 < 1 / 1 e 21 3e8/1e31<1/1e21 3e8/1e31<1/1e21

具体细可参考代码。

A:dir -C (二分答案+单调队列优化dp)

首先容易看出要二分答案,因为对于 l 1 l_1 l1< l 2 l_2 l2,如果 l 1 l_1 l1符合要求,那么 l 2 l_2 l2一定符合要求。那么对于一个答案k,如何check呢?
考虑如下的dp:
d p i dp_i dpi 为以 i i i 作为某block结尾,此block之前(包括此block)的宽度之和。则转移方程:
d p i = m i n ( d p j + m a x [ j + 1 , i ] ) , i − k ≤ j < i dp_i=min(dp_j+max[j+1,i]),i-k \le j < i dpi=min(dpj+max[j+1,i]),ikj<i.
其中 m a x [ l , r ] max[l,r] max[l,r] 代表 l ≤ i ≤ r l \le i \le r lir 中最大的 f i f_i fi.
容易知道dp序列单调不降。
从枚举 [ j + 1 , i ] [j+1,i] [j+1,i]中的最大值入手。对于当前 i i i,首先考虑 [ i − k + 1 , i ] [i-k+1,i] [ik+1,i] 中的最大值为转移到 d p i dp_i dpi 中的 m a x [ j + 1 , i ] max[j+1,i] max[j+1,i] ,这个最大值所在位置记为 i 1 i_1 i1 ,则对应的 d p j dp_j dpj 可以取到 i − k ≤ j < i 1 i-k \le j ikj<i1,由于dp单调不降,自然是取 d p i − k 最 优 dp_{i-k}最优 dpik ; 继续,将区间缩为 [ i 1 + 1 , i ] [i_1+1,i] [i1+1,i],此时区间内最大值记为 i 2 i_2 i2,则对应地取 d p i 1 dp_{i1} dpi1 ; 一直这样下去直到区间变为 [ i + 1 , i ] [i+1,i] [i+1,i]
可知上面的 i 1 i_1 i1, i 2 i_2 i2, i 3 i_3 i3, i 4 i_4 i4 序列单调不增。于是可以在遍历的过程中用单调栈维护此序列。序列每当新加入或删除元素,其对应的贡献(即 d p j + m a x [ j + 1 , i ] dp_j + max[j+1,i] dpj+max[j+1,i])也要更新。维护所有贡献可以用优先队列 or 线段树等。最后check的就是 d p n − 1 ≤ w dp_n -1 \le w dpn1w
一次check的复杂度O( n l o g n nlogn nlogn),总时间复杂度O( n l o g 2 n nlog^2n nlog2n )。

代码

E

#include
int main(){
    puts("12:00");
    puts("2022/5/21"); 
}

D

#include
using namespace std;
int a,b;
int main(){
    cin>>a>>b;
    if(a==0)cout<<0;
    else if(b==0)cout<<0;
    else if(b==1)cout<<a;
    else cout<<"INF";
}

G

#include
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define IOS {cin.tie(0); cout.tie(0); ios::sync_with_stdio(0);}
const int N=1e6,mod=100;
int ans[N+5];
int main(){
    IOS
    ans[1]=1; ans[2]=1;
    rep(i,3,N)ans[i]=(ans[i-1]+ans[i-2])%mod;
    int q,x;
    cin>>q;
    while(q--){
        cin>>x;
        cout<<ans[x]/10<<'\n';
    }
}
N
#include 
#include 
#include 
#include 
using namespace std;
char ch[52][142];
int n;
inline void solve() {
    scanf("%d", &n);
    for (int i = 1; i <= n * 2 + n / 4; ++i)
        for (int j = 1; j <= 6 * n + n / 2; ++j)
            ch[i][j] = ' ';
    for (int x = 1; x <= (n / 2 + n / 4); ++x) 
        for (int i = 1; i <= n; ++i)
            ch[x][i + (x - 1)] = '1';
    for (int x = 1; x <= (n * 2 + n / 2); ++x) 
        for (int i = 1; i <= n; ++i)
            ch[x][i + n * 2 - (x - 1)] = '1',
            ch[x][i + n * 3 + n / 2 - (x - 1)] = '1',
            ch[x][i + n * 5 + n / 2 - (x - 1)] = '1';
    for (int x = (n / 2) + 1; x <= (n / 2) + (n / 4); ++x)
        for (int i = 1; i <= n; ++i)
            ch[x][i + n * 4 + (n >> 1) - (x - 1)] = '1';
    for (int i = 1; i <= n + n / 4; ++i) {
        for (int j = 1; j <= (6 * n + n / 2 - (i - 1)); ++j)   
            putchar(ch[i][j]); 
        puts("");
    }
}
inline void init() {

}
int main() {
    init(), solve();
    return 0;
}
B
#include 
#include 
#include 
using namespace std;

typedef long long ll;
const int mn = 1000010;

int n, m;
int A[mn], s2[mn], s1[mn], b[mn];

int main() {
    scanf("%d%d", &m, &n);
    for(int i = 1; i <= m; ++i) scanf("%d", b + i);
    for(int i = 1; i <= n; ++i) scanf("%d", A + i);
    for(int i = 1; i <= 100000; ++i) s1[i] = n + 1, s2[i] = 0;
    for(int i = 1; i <= n; ++i) 
        s1[A[i]] = min(s1[A[i]], i), 
        s2[A[i]] = max(s2[A[i]], i);
    long long sum = 0;
    for(int i = 1; i <= m; ++i) 
        sum += 1ll * max(s1[b[i]], (n - s2[b[i]] + 1));
    printf("%lld\n", sum);
    return 0;
}
F
#include 
#include 
#include 
#include 
using namespace std;

typedef long long ll;
const int mn = 100010;

int fa[55], n, m;
string str;

int main() {    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m;
    cin >> str;
    char c1[10], c2[10];
    for(int _ = 1, opt; _ <= m; ++_) {
        cin >> opt;
        if(opt == 1) {
            cin >> c1 >> c2;
            int x1 = c1[0] >= 'a' ? c1[0] - 'a' + 26 : c1[0] - 'A';
            int x2 = c2[0] >= 'a' ? c2[0] - 'a' + 26 : c2[0] - 'A';
            for(int i = 0; i < 52; ++i) 
                if(fa[i] == x1) fa[i] = x2;
        } else if(opt == 2) {
            cin >> str; 
            for(int i = 0; i < 52; ++i)
                fa[i] = i;
            } 
    }
    int nn = str.size();
    for(int i = 0; i < nn; ++i) {
        int x1 = str[i] >= 'a' ? str[i] - 'a' + 26 : str[i] - 'A';
        char c2 = fa[x1] >= 26 ? fa[x1] - 26 + 'a' : fa[x1] + 'A';
        str[i] = c2;
    }
    cout << str;
    return 0;
}
H
#include 
#include 

const int mn = 100010;

int n, m, l, k;
struct edge {
    int v, nxt;
} e[mn << 1];
int h[mn], p = -1;
inline void add(int a, int b) { e[++p].nxt = h[a], h[a] = p, e[p].v = b; }

bool vis[mn], col[mn];
void dfs(int x, int c) {
    if(vis[x]) return;
    col[x] = c;
    vis[x] = 1;
    for(int i = h[x]; i != -1; i = e[i].nxt) {
        int v = e[i].v;
        dfs(v, c ^ 1);
    }
}

int main() {
    memset(h, -1, sizeof h);
    scanf("%d%d%d%d", &n, &m, &l, &k);
    for(int x, y, i = 0; i < m; ++i) {
        scanf("%d%d", &x, &y);
        add(x, y), add(y, x);
    }
    dfs(l, k);
    for(int i = 1; i <= n; ++i) printf("%d%c", col[i], " \n"[i == n]);
    return 0;
}
I
#include
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define IOS {cin.tie(0); cout.tie(0); ios::sync_with_stdio(0);}
#define ll long long
#define P pair<int,int>
const int N=3e3+5,inf=2e9;
int n,t[N];
int res=inf,idx,sum;
int main(){
    IOS
    cin>>n;
    rep(i,1,n)cin>>t[i];
    sort(t+1,t+n+1);
    rep(i,(n+1)/2,n){ //招i-1个人
        int two=n-i,one=i-two;
        int minv=t[n-one+1],maxv=t[n];  //当one=0,不能这么定义!!下面修改
        if(one==0){
            minv=inf;maxv=0;
        }
        rep(j,1,two){
            sum=t[j]+t[2*two+1-j];
            minv=min(minv,sum); maxv=max(maxv,sum);
        }
        if(res>=maxv-minv){
            res=maxv-minv;idx=i;
        }
    }
    cout<<res;
}
J
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long ll;
const int mn = 505;
const ll inf = (1ll << 60);

int n, m, p[mn];
ll mp[mn][mn], ans[mn];
bool vis[mn];
set<int> se;

int main() {
    memset(vis, 0, sizeof(vis));
    memset(mp, 0x3f, sizeof(mp));
    scanf("%d%d", &n, &m);
    for (int i = 2; i <= n; ++i)
        for (int j = 1; j < i; ++j)
            scanf("%lld", &mp[i][j]), mp[j][i] = mp[i][j];
    for (int i = 1; i <= n; ++i)
        mp[i][i] = 0;
    for (int i = 1; i <= m; ++i)
        scanf("%d", p + i), vis[p[i]] = 1;

    for (int k = 1; k <= n; ++k) 
        if (!vis[k])
            for (int i = 1; i <= n; ++i) 
                for (int j = 1; j <= n; ++j) 
                    if (i != j && j != k && i != k)
                        if (mp[i][j] > mp[i][k] + mp[k][j])
                            mp[i][j] = mp[i][k] + mp[k][j];
    
    for (int i = 1; i <= n; ++i)
        vis[i] = !vis[i];

    ll an = 0ll;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j < i; ++j)
            if (vis[i] && vis[j])
                an ^= mp[i][j];
    ans[m + 1] = an;

    for (int k, l = m; l > 0; --l) {
        k = p[l], vis[k] = 1;
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n; ++j)
                if (i != j && j != k && i != k)
                    if (mp[i][j] > mp[i][k] + mp[k][j])
                        mp[i][j] = mp[i][k] + mp[k][j];

        an = 0ll;
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j < i; ++j)
                if (vis[i] && vis[j])
                    an ^= mp[i][j];
        ans[l] = an;
    }
    for(int i = 2; i <= m + 1; ++i)
        printf("%lld%c", ans[i], " \n"[i == m + 1]);
    return 0;
}
L
#include 
#include 
#include 
#include 
using namespace std;

typedef long long ll;
const int mn = 100010;
const ll mod = 998244353;

#define lson l, mm, rt << 1
#define rson mm + 1, r, rt << 1 | 1
#define bson l, r, rt
#define root 1, n, 1
#define Max_M ((1 << m) - 1)

int z[mn << 2], col[mn << 2];
int n, m, K;
inline void update(int rt) { z[rt] = z[rt << 1] | z[rt << 1 | 1]; }

inline void color(int l, int r, int rt, int v) { z[rt] &= v, col[rt] &= v; }

inline void push_col(int l, int r, int rt) {
    if(col[rt] != ((1 << m) - 1)) { 
        int mm = (l + r) >> 1;
        color(lson, col[rt]);
        color(rson, col[rt]);
        col[rt] = ((1 << m) - 1);
    }
}

void build(int l, int r, int rt) {
    col[rt] = (1 << m) - 1;
    if(l == r) { z[rt] = (1 << m) - 1; return; }
    int mm = (l + r) >> 1;
    build(lson), build(rson), update(rt);
}

void modify(int l, int r, int rt, int nowl, int nowr, int v) {
    if(nowl <= l && r <= nowr) { color(bson, v); return; }
    int mm = (l + r) >> 1;
    push_col(bson);
    if(nowl <= mm) modify(lson, nowl, nowr, v);
    if(mm < nowr)  modify(rson, nowl, nowr, v);
    update(rt);
}

int query(int l, int r, int rt, int nowl, int nowr) {
    if(nowl <= l && r <= nowr) return z[rt];
    int mm = (l + r) >> 1;
    push_col(bson);
    if(nowl <= mm) {
        if(mm < nowr) return query(lson, nowl, nowr) | query(rson, nowl, nowr);
        else return query(lson, nowl, nowr);
    } else return query(rson, nowl, nowr);
}

int main() {
    scanf("%d%d%d", &n, &m, &K);
    build(root);
    for (int x, l, r, op, k, _ = 1; _ <= K; ++_) {
        scanf("%d%d%d%d", &op, &l, &r, &k);
        if (!op) {
            x = Max_M;
            for (int j, i = 0; i < k; ++i)
                scanf("%d", &j), x ^= (1 << (j - 1));
            modify(root, l, r, x);
        } else {
            x = query(root, l, r);
            int ans = 0;
            for (int j, i = 0; i < k; ++i)
                scanf("%d", &j), 
                ans += ((x & (1 << (j - 1))) ? 1 : 0);
            if (ans == 0) puts("G");
            else printf("%d\n", ans);
        }
    }
    return 0;
}

C

#include
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define IOS {cin.tie(0); cout.tie(0); ios::sync_with_stdio(0);}
#define ll long long
#define P pair<int,int>
unordered_map<ll,int>mp;
const int N=1005,inf=4e5+5;
int n,ans,cnt,Gcd;
pair<ll,P> q[N*N/2];
P p[N],temp;
ll Hash(int x,int y){  //将两个[-2e5,2e5]范围内的整数加密,记录一对(x,y)
    return 1ll*x*inf+y;
}
int main(){
    IOS
    cin>>n;
    rep(i,1,n){
        cin>>p[i].first>>p[i].second;
    }
    rep(i,1,n){
        rep(j,i+1,n){
            q[++cnt].first=Hash(p[i].first+p[j].first,p[i].second+p[j].second);
            temp={p[i].first-p[j].first,p[i].second-p[j].second};
            if(temp.first==0)temp.second=1;  //(0,1)
            else if(temp.second==0)temp.first=1; //(1,0)
            else{ //(x,y),满足x,y互质,且y>0
                Gcd=__gcd(abs(temp.first),abs(temp.second));
                temp.first/=Gcd; temp.second/=Gcd;
                if(temp.second<0){
                    temp.first=-temp.first;temp.second=-temp.second;
                }
            }
            q[cnt].second=temp;
        }
    }
    sort(q+1,q+cnt+1);
    q[0].first=Hash(inf,inf);
    rep(i,1,cnt){
        if(q[i].first!=q[i-1].first)mp.clear(); //对于不同类要clear
        ans+=mp[Hash(-q[i].second.second,q[i].second.first)];
        mp[Hash(q[i].second.first,q[i].second.second)]++;
    }
    cout<<ans;
}

M

#include
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define IOS {cin.tie(0); cout.tie(0); ios::sync_with_stdio(0);}
#define ll long long
#define P pair<int,int>
#define vi vector<int>
const int N=1e5+5;
vector<P>tu[N];
int n,k,u,v,w,dp[N],f[N],d[N];
void Max(int & a,int b){a=max(a,b);}
void dfs(int now,int pre){
    P f1={0,0},f2={0,0},d1={0,0},d2={0,0},d3={0,0}; //置为0(不要为负),顺带处理了子树为空树的转移情况.
    for(P k:tu[now]){
        int son=k.first;
        if(son==pre)continue;
        dfs(son,now);

        Max(dp[now],dp[son]+k.second);
        int temp=d[son]+k.second;
        Max(d[now],temp);
        if(temp>d1.first){
            d3=d2;d2=d1;d1={temp,son};
        }
        else if(temp>d2.first){
            d3=d2;d2={temp,son};
        }
        else if(temp>d3.first){
            d3={temp,son};
        }

        Max(f[now],f[son]);
        if(f[son]>f1.first){
            f2=f1;f1={f[son],son};
        }
        else if(f[son]>f2.first){
            f2={f[son],son};
        }
    }
    Max(f[now],d1.first+d2.first);
    
    int maxf,sumd;
    for(P k:tu[now]){
        int son=k.first;
        if(son==pre)continue;
        if(son==f1.second)maxf=f2.first;
        else maxf=f1.first;
        if(son==d1.second)sumd=d2.first+d3.first;
        else if(son==d2.second)sumd=d1.first+d3.first;
        else sumd=d1.first+d2.first;
        Max(dp[now],d[son]+k.second+max(maxf,sumd));
    }
}
int main(){
    IOS
    // freopen("in.txt","r",stdin);
    cin>>n>>k;
    per(i,n-1,1){
        cin>>u>>v>>w;
        tu[u].push_back({v,w});
        tu[v].push_back({u,w});
    }
    dfs(k,0);
    cout<<dp[k];
}

K

#include
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define IOS {cin.tie(0); cout.tie(0); ios::sync_with_stdio(0);}
#define ll long long
#define P pair<int,int>
const int N=1e7,mod=1e9+7;
int pri[N+5],notpri[N+5],cnt;
int num[N+5],py[N+5];//num[i]:素因子i的个数,py是num的拷贝
int q,y; ll n;
P card[1000005];
int qp(int x,int y){ //快速幂
    int res=1;
    while(y){
        if(y&1)res=1ll*res*x%mod;
        x=1ll*x*x%mod;
        y>>=1;
    }
    return res;
}
struct frac{//分数结构体
    ll x; __int128_t y; //分子,分母
    bool operator < (const frac & t)const &{
        return x*t.y<t.x*y;
    }
    int val(){  //取模结果
        return x%mod*qp(y%mod,mod-2)%mod;
    }
};
int main(){
    IOS
    rep(i,2,N){ //线性筛法求素数
        if(!notpri[i])pri[++cnt]=i;
        ll temp;
        for(int j=1;temp=1ll*i*pri[j],temp<=N;++j){
            notpri[temp]=1;
            if(i%pri[j]==0)break;
        }
    }
    cin>>n>>q;
    ll t1=1,nn=n;
    //处理初始n的素因子
    rep(i,1,cnt){
        while(nn%pri[i]==0){
            nn/=pri[i]; ++num[pri[i]];
        }
        t1*=(num[pri[i]]+1);
        py[pri[i]]=num[pri[i]];
    }
    if(nn>1)t1*=2;
    rep(i,1,q)cin>>card[i].first>>card[i].second;
    sort(card+1,card+q+1);
    frac res={1,1};
    per(i,q,1){  //倒序,先操作x=1的卡片
        int temp=card[i].second;
        if(card[i].first){
            frac ret; ret.y=(__int128_t)card[i].second*n; ret.x=t1;
            rep(j,1,446){
                if(temp%pri[j]==0){
                    int cnt=0;
                    while(temp%pri[j]==0){
                        ++cnt; temp/=pri[j];
                    }
                    ret.x/=py[pri[j]]+1;
                    ret.x*=py[pri[j]]+1+cnt;
                    num[pri[j]]+=cnt;
                }
            }
            if(temp>1){
                ret.x/=py[temp]+1;
                ret.x*=py[temp]+2;
                ++num[temp];
            }
            if(ret<res)res=ret;
        }
        else{
            rep(j,1,446){
                if(temp%pri[j]==0){
                    int cnt=0;
                    while(temp%pri[j]==0){
                        ++cnt; temp/=pri[j];
                    }
                    num[pri[j]]=max(0,num[pri[j]]-cnt);
                }
            }
            if(temp>1)num[temp]=max(0,num[temp]-1);
        }
    }
    int t3=1; __int128_t t2=1; //因子个数 , 最终n
    const __int128_t M=1e31;
    int flag=0; //最终的数是否爆1e31  实际数据没怎么卡,1e24就过了
    rep(i,1,cnt){
        if(flag)break;
        t3*=num[pri[i]]+1;
        rep(j,1,num[pri[i]]){
            t2*=pri[i];
            if(t2>M){flag=1;break;}
        }
    }
    if(nn>1)t3*=2,t2=t2%mod*nn;
    frac ans=frac{t3,t2};
    //两种选择: 1、第一轮declare:res   2、选完所有card:ans
    if(!card[q].first){  //没有x=1的卡片,Alice直接等所有选完即可.
        cout<<ans.val();
    }
    else if(flag){ //最终n过大,一定不如第一轮选更优  
        cout<<res.val();
    }
    else{   //比较t3/t2 与 res
        if(ans<res)cout<<res.val();
        else cout<<ans.val(); 
    }
}
/*
int128范围内的一个数的最大因子数: (可以如下判断)
cout<

A

#include
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define IOS {cin.tie(0); cout.tie(0); ios::sync_with_stdio(0);}
#define ll long long
#define P pair<ll,int>
const int N=1e5+5;
int n,w,q[N],l,r,f[N];
ll v[N],dp[N];  //v[]用来判断堆中的值是否仍合法
bool check(int k){
    l=1,r=0;
    priority_queue<P>Q;
    rep(i,1,n){
        if(l<=r && q[l]<i-k+1){
            v[q[l]]=0; l++;
        }
        if(l<=r){ //每次都要更新最左端的,因为i-k在变换!
            v[q[l]]=f[q[l]]+1+dp[max(i-k,0)]; Q.push({-v[q[l]],q[l]});
        }
        while(l<=r && f[i]>=f[q[r]]){
            v[q[r]]=0; r--;
        }
        if(l<=r){
            v[i]=dp[q[r]]+f[i]+1;
            q[++r]=i;
        }
        else{
            v[i]=dp[max(i-k,0)]+f[i]+1;
            q[++r]=i;
        }
        Q.push({-v[i],i});
        P tem;
        while(Q.size()){
            tem=Q.top();
            if(v[tem.second]!=-tem.first)Q.pop();
            else break;
        }
        dp[i]=-tem.first;
    }
    return dp[n]-1<=w;
}
int main(){
    IOS
    cin>>n>>w;
    rep(i,1,n)cin>>f[i];
    int L=1,R=n,mid;
    while(L<=R){
        mid=(L+R)>>1;
        if(check(mid))R=mid-1;
        else L=mid+1;
    }
    cout<<L;
}

你可能感兴趣的:(学习)