CF1771

CF1771

CF1771A

link

CF1771A题意

有一个长度为 n n n 的序列 a 1 , 2 , . . . , n a_{1,2,...,n} a1,2,...,n,需要求出这个序列中一共有多少对点对满足

  • 1 ≤ i , j ≤ n 1\le i,j\le n 1i,jn
  • i ≠ j i\neq j i=j
  • ∣ a i − a j ∣ = max ⁡ 1 ≤ p , q ≤ n ∣ a p − a q ∣ |a_i-a_j|=\max_{1\le p,q\le n}|a_p-a_q| aiaj=max1p,qnapaq

CF1771A题解

不难发现 max ⁡ 1 ≤ p , q ≤ n ∣ a p − a q ∣ \max_{1\le p,q\le n}|a_p-a_q| max1p,qnapaq 其实就是序列中最大值减去最小值
然后又发现只有当 a i , a j a_i,a_j ai,aj 分别是最大值和最小值的时候才能满足这个柿子
然后就没有然后了
不过需要注意一下,如果整个序列所有数相同的话,答案是 n × ( n − 1 ) n\times (n-1) n×(n1) 需要特判

CF1771A代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
    int x = 0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
const int maxn = 1e5 + 10;
int n;
signed main(){
    int T = read();
while(T--){
    n = read();int mx = -1,mxtime = 0,mn = 0x3f3f3f3f,mntime = 0,u;
    for(int i = 1;i <= n;i++){
        u = read();
        if(u > mx){mxtime = 1;mx = u;}
        else if(u == mx){mxtime++;}
        if(u < mn){mntime = 1;mn = u;}
        else if(u == mn){mntime++;}
    }
    if(mn != mx)printf("%lld\n",mxtime * mntime * 2ll);
    else printf("%lld\n",n * (n - 1));
}
    return 0;
}

CF1771B

link

CF1771B题意

给出 n , m n,m n,m m m m 个限制 x i , y i x_i,y_i xi,yi(注意,不保证 x i ≤ y i x_i\le y_i xiyi
现在要求求出满足以下要求的点对 i , j i,j i,j 的数量

  • 1 ≤ i ≤ j ≤ n 1\le i\le j\le n 1ijn
  • ∃ k , i ≤ x k , y k ≤ j \exist k,i\le x_k,y_k\le j k,ixk,ykj

CF1771B题解

这个题其实很简单,考虑从 n → 1 n\to 1 n1 的枚举 i i i,统计有多少个 i ≤ j i\le j ij 满足要求
不妨让 x i ≤ y i x_i\le y_i xiyi
发现只要 j j j 小于所有的 y k ( i ≤ x k ) y_k(i\le x_k) yk(ixk),那么这个 j j j 就是合法的
同时发现这玩应对于一个 i i i,只要 j j j 满足,那么 i ≤ k ≤ j i\le k\le j ikj 都会满足
然后就没了所以为什么翻译里面没说 x i , y i x_i,y_i xi,yi 的关系啊(

CF1771B代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
    int x = 0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
const int maxn = 1e5 + 10;
int n, m;
vector<int> vec[maxn];
signed main(){
int T = read();
while(T--){
    n = read();m = read();int u, v;
    for(int i = 1;i <= n;i++)vec[i].clear();
    for(int i = 1;i <= m;i++){u = read(); v = read();vec[min(u,v)].push_back(max(u,v));}
    int lst = n + 1,ans = 0;
    for(int i = n;i;i--){
        if(!vec[i].empty()){for(int j : vec[i])lst = min(lst,j);}
        ans += lst - i;
    }
    printf("%lld\n",ans);
}
    return 0;
}

CF1771C

link

CF1771C题意

有一个长度为 n n n 的数组 a 1 , 2 , . . . , n a_{1,2,...,n} a1,2,...,n,问这个数组中是否存在一对点 i , j i,j i,j 满足 gcd ⁡ ( a i , a j ) ≠ 1 \gcd(a_i,a_j) \neq 1 gcd(ai,aj)=1

CF1771C题解

很自然想到将每一个数字都质因数分解,然后放入 m a p map map 中,如果有相同的就 “YES”

CF1771C代码

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x = 0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
const int maxn = 1e5 + 10;
int n, a[maxn];
int prime[maxn], tot;
bool book[maxn];
map<int,int> mp;
signed main(){
    for(int i = 2;i <= 1e5;i++){
        if(!book[i]){prime[++tot] = i;}
        for(int j = 1;j <= tot && i * prime[j] <= 1e5;j++){
            book[i * prime[j]] = 1;
            if(i % prime[j] == 0)break;
        }
    }
int T = read();
while(T--){
    n = read();mp.clear();
    for(int i = 1;i <= n;i++){a[i] = read();}
    bool flag = false;
    for(int i = 1;i <= n && !flag;i++){
        int tmp = a[i];
        for(int j = 1;j <= tot && prime[j] <= tmp;j++){
            if(tmp % prime[j] == 0){
                if(mp[prime[j]] == 1){flag = true;break;}
                mp[prime[j]] = 1;
                while(tmp % prime[j] == 0)tmp /= prime[j];
            }
        }
        if(tmp != 1){
            if(mp[tmp]){flag = true;break;}
            mp[tmp] = 1;
        }
    }
    puts(flag ? "YES" : "NO");
}
    return 0;
}

CF1771D

CF1771D题意

给出一棵树 T T T(节点数 ≤ 2 × 1 0 3 \le 2\times10^3 2×103),每个树上有一个字母,问所有路径上的最大回文串子序列长是多少。

CF1771D题解

先考虑下序列上的怎么做。
f i , j f_{i,j} fi,j 表示 [ i , j ] [i,j] [i,j] 的最大回文串子序列长度。
那么有 f i , j = max ⁡ f i + 1 , j , f i , j − 1 , f i + 1 , j − 1 + ( c h i = = c h j ) × 2 f_{i,j} = \max{f_{i+1,j},f_{i,j-1},f_{i+1,j-1}+(ch_i==ch_j)\times 2} fi,j=maxfi+1,j,fi,j1,fi+1,j1+(chi==chj)×2
那树上的怎么办呢?
不妨设 f a i , j fa_{i,j} fai,j 表示当这棵树以 j j j 为根时 i i i 的父亲节点。
那么显然有 f i , j = max ⁡ f f a i , j , j , f i , f a j , i , f f a i , j , f a j , i + ( c h i = = c h j ) × 2 f_{i,j} = \max{f_{fa_{i,j},j},f_{i,fa_{j,i}},f_{fa_{i,j},fa_{j,i}}+(ch_i==ch_j)\times 2} fi,j=maxffai,j,j,fi,faj,i,ffai,j,faj,i+(chi==chj)×2
更新顺序?
序列上的时候按照两点距离就好了,排下序就完了。
然后就会喜提 T L E   o n   T e s t 15 TLE\space on\space Test15 TLE on Test15
为什么呢,因为 O ( n 2 × log ⁡ 2 n ) O(n^2\times \log_2n) O(n2×log2n) 肥肠难以通过
那怎么办呢?
想到,如果想要更新一对点 ( i , j ) (i,j) (i,j),就必须更新所有这条路径上的点。
然而这玩应可以记忆化搜索解决
然后就没有然后了

CF1771D代码

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    // int x = 0, f = 1;char ch = getchar();
    // while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
    // while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    // return x * f;
    int x;scanf("%d",&x);return x;
}
const int maxn = 2e3 + 10;
int n;char ch[maxn];

int fa[maxn][maxn], f[maxn][maxn];
vector<int> edg[maxn];
void dfs(int u,int fat,int root){
    fa[u][root] = fat;
    for(int v : edg[u])
        if(v != fat){dfs(v, u,root);}
}

int calc(int u,int v){
    if(u == v)return 1;
    if(u == fa[v][1] || v == fa[u][1]){return 1 + (ch[u] == ch[v]);}
    if(f[u][v])return f[u][v];
    return f[u][v] = max(calc(u,fa[v][u]),max(calc(fa[u][v],v),calc(fa[u][v],fa[v][u]) + (ch[u] == ch[v] ? 2 : 0)));
}

signed main(){
    // #ifndef ONLINE_JUDGE
    // freopen("test.in","r",stdin);
    // #endif
int T = read();
while(T--){
    n = read();scanf("%s",ch + 1);
    for(int i = 1;i <= n;i++){edg[i].clear();}
    for(int i = 1;i < n;i++){
        int u = read(), v = read();
        edg[u].push_back(v);edg[v].push_back(u);
    }
    for(int i = 1;i <= n;i++){
        dfs(i,i,i);
        for(int j = 1;j <= n;j++)f[i][j] = 0;
    }
    int ans = 0;
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++)
            ans = max(ans,calc(i,j));
    printf("%d\n",ans);
}
    return 0;
}

CF1771E

link

CF1771E题意

给出一个地图( n × m n\times m n×m,其中 1 ≤ n , m ≤ 400 1\le n,m\le 400 1n,m400),其中有三种字符 “ . . .”,“ # \# #” 和 “ m m m
要求你在这张地图中画出一个 " H H H"字,具体要求如下

  1. " H H H"字的左上角和右上角需要在同一行上,左下角和右下角需要在同一行上
  2. " H H H"字不能经过 “ # \# #
  3. " H H H"字至多能经过一个 “ m m m
  4. " H H H"字可以经过任意个 “ . . .

请问怎样画 “ H H H” 字使得它所覆盖的点最大

CF1771E题解

种花?
这不就是种CCF的翻版吗
发现这个数据范围很小,可以不用乱七八糟的预处理,也不用考虑预处理的时间复杂度
u p i , j , 0 / 1 up_{i,j,0/1} upi,j,0/1 表示从 ( i , j ) (i,j) (i,j) 点向上走 经过/不经过 “ m m m” 能走多远
d o w n i , j , 0 / 1 down_{i,j,0/1} downi,j,0/1 表示从 ( i , j ) (i,j) (i,j) 点向下走 经过/不经过 “ m m m” 能走多远
注意,这里的预处理不包括点 ( i , j ) (i,j) (i,j)
然后更新答案的时候直接暴力枚举 “ H H H” 字的左右交点,然后维护一下交点之间有没有 “ m m m”,没有的话可以把经过的机会依次给四个竖线比一下就好了
细节是真的多吃我好几发提交

CF1771E代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 400 + 10;
int n, m;char ch[maxn][maxn];
int up[maxn][maxn][2], down[maxn][maxn][2];

signed main(){
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i++){scanf("%s",ch[i] + 1);}
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= m;j++){
            bool passed = 0, stop = 0;
            up[i][j][0] = up[i][j][1] = down[i][j][0] = down[i][j][1] = 0;
            for(int k = i - 1;k && !stop;k--){
                switch(ch[k][j]){
                    case '.':
                        if(!passed)up[i][j][0]++;
                        up[i][j][1]++;
                    break;
                    case '#':
                        stop = 1;
                    break;
                    case 'm':
                        if(passed){stop = 1;break;}
                        passed = 1; up[i][j][1]++;
                    break;
                    default:break;
                }
            }
            passed = 0;stop = 0;
            for(int k = i + 1;k <= n && !stop;k++){
                switch(ch[k][j]){
                    case '.':
                        if(!passed)down[i][j][0]++;
                        down[i][j][1]++;
                    break;
                    case '#':
                        stop = 1;
                    break;
                    case 'm':
                        if(passed){stop = 1;break;}
                        passed = 1; down[i][j][1]++;
                    break;
                    default:break;
                }
            }
            if(!up[i][j][0])up[i][j][0] = -114514;
            if(!up[i][j][1])up[i][j][1] = -114514;
            if(!down[i][j][0])down[i][j][0] = -114514;
            if(!down[i][j][1])down[i][j][1] = -114514;
        }
    }
    int ans = 0;
    for(int i = 1;i <= n;i++){
        for(int j = 3;j <= m;j++){
            bool passed = (ch[i][j] == 'm');
            if(ch[i][j] == '#' || ch[i][j - 1] == '#')continue;
            if((ch[i][j - 1] == 'm')){if(passed)continue;passed = 1;}
            for(int k = j - 2;k;k--){
                if(ch[i][k] == '#')break;
                if(ch[i][k] == 'm'){if(passed)break;passed = 1;}

                int up1 = min(up[i][k][0],up[i][j][0]);
                int down1 = min(down[i][k][0],down[i][j][0]);
                if(!passed){
                    int up2 = max(min(up[i][k][0],up[i][j][1]),min(up[i][k][1],up[i][j][0]));
                    int down2 = max(min(down[i][k][0],down[i][j][1]),min(down[i][k][1],down[i][j][0]));
                    ans = max(ans,j - k + 1 + 2 * max(up1 + down2,up2 + down1));
                }
                ans = max(ans,j - k + 1 + 2 * (up1 + down1));

            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

CF1771F

link

CF1771F题意

给出一个非负整数序列 a 1 , 2 , . . . , n a_{1,2,...,n} a1,2,...,n
每次询问 [ l , r ] [l,r] [l,r] 区间内出现次数为奇数的最小数字,没有扣眼珠子输出0
强制在线

CF1771F题解

首先先想一下如果没有出现次数是奇数的限制怎么做。
还用废话啊主席树就完了呗
但是有了这个限制怎么办呢。
有么有一种方法,能够让一个数出现偶数次就凭空消失呢。
x o r xor xor
如果一个数 x o r xor xor 自己偶数次,那就会凭空消失,而 x o r xor xor 自己奇数次却不会。
那这就好办了,搞一个可持久化权值线段树分治,每个位置维护的是自己所在区间的 x o r xor xor 值,查询的时候只需要判断一下左儿子的 x o r xor xor 值是不是 0 0 0,如果不是就进入左儿子,是就进入右儿子。
然后你就开心的交了上去。

  \space     \space     \space     \space     \space     \space     \space     \space     \space     \space   恭喜!
W A WA WA 了这道题!
为啥呢是不是自己哪里写挂了
在调了 1 ′ 14 5 ′ 14 1 ′ 91 9 ′ 810 s 1'145'141'919'810s 1145141919810s 后你不禁想到了一个问题:

  • 1   x o r   2   x o r   3 = 0 1\space xor \space 2 \space xor \space 3 = 0 1 xor 2 xor 3=0

?!xor你演我
那肿莫办呢?原神,启动!
那好办,给每个值在线段树上 x o r xor xor 的时候 x o r xor xor 一个随机数,这样就不会出现被演的情况了。

CF1771F代码

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x = 0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
const int maxn = 2e5 + 10, mod = (1 << 30);
int n, m;
int a[maxn];
map<int,int> mp;
int head[maxn];
struct Segment_Tree{
    int tot;
    struct node{
        int lson, rson, sum;
        node(int sum = 0,int lson = 0,int rson = 0):lson(lson),rson(rson),sum(sum){}
    }d[maxn * 100];
    node mergenode(node l,node r,node f = node()){return node(l.sum ^ r.sum,f.lson,f.rson);}
    int update(int lst,int pos,int x,int l = 1,int r = mod){
        int u = ++tot;d[u] = d[lst];
        if(l == r){d[u].sum ^= x;return u;}
        int mid = l + r >> 1;
        if(pos <= mid)d[u].lson = update(d[lst].lson,pos,x,l,mid);
        else d[u].rson = update(d[lst].rson,pos,x,mid + 1,r);
        d[u] = mergenode(d[d[u].lson],d[d[u].rson],d[u]);
        return u;
    }
    int query(int now,int lst,int l = 1,int r = mod){
        if(l == r){return d[now].sum ^ d[lst].sum ? l : 0;}
        int mid = l + r >> 1, tmp = d[d[now].lson].sum ^ d[d[lst].lson].sum;
        if(tmp)return query(d[now].lson,d[lst].lson,l,mid);
        else return query(d[now].rson,d[lst].rson,mid + 1,r);
    }
}tree;

signed main(){
    n = read();
    mt19937 rand(time(0));
    for(int i = 1;i <= n;i++){
        a[i] = read();
        if(mp.count(a[i]))continue;
        mp[a[i]] = (rand() & mod - 1) + 1;
    }
    for(int i = 1;i <= n;i++){head[i] = tree.update(head[i - 1],a[i],mp[a[i]]);}
    m = read();int l, r, lastans = 0;
    for(int i = 1;i <= m;i++){
        l = read() ^ lastans; r = read() ^ lastans;
        printf("%d\n",lastans = tree.query(head[r],head[l - 1]));
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值