Processing math: 4%

Codeforces 杂题 round 1

目录:

Edu103(div.2) E

cf round700(div.2) C

cf round683(div.2) D

cf round683(div.2) E

cf round666(div.2) D

cf round707(div.2) C

Edu82(div.2) D

cf round391(div.1+2) G


Edu103(div.2) E

考虑怎么匹配字符串,关键在于通配符

可以根据 n 个含有通配符的串建立 trie 树,然后枚举 mt 串通配符的位置,显然每个串有 2k 种情况,那么就进行 2^k\times m 次匹配

题目要求位于 k 的串第一个被匹配,那么正难则反地去做,可以保证其他串都在他之前出现,这个可以连边转化为闭合子图,然后 topo 序再反过来就是答案

复制代码
  1 #include <ctime>
  2 #include <cmath>
  3 #include <cctype>
  4 #include <cstdio>
  5 #include <cstring>
  6 #include <cstdlib>
  7 #include <iostream>
  8 #include <algorithm>
  9 #include <vector>
 10 #include <queue>
 11 #include <bitset>
 12 #define inf 100010
 13 #define INF 0x7fffffff
 14 #define ll long long
 15 
 16 template <class I>
 17 inline void read(I &num){
 18     num = 0; char c = getchar(), up = c;
 19     while(!isdigit(c)) up = c, c = getchar();
 20     while(isdigit(c)) num = (num << 1) + (num << 3) + (c ^ '0'), c = getchar();
 21     up == '-' ? num = -num : 0; return;
 22 }
 23 template <class I>
 24 inline void read(I &a, I &b) {read(a); read(b);}
 25 template <class I>
 26 inline void read(I &a, I &b, I &c) {read(a); read(b); read(c);}
 27 
 28 struct edge {
 29     int to;
 30     edge *nxt;
 31 };
 32 
 33 edge *g[inf];
 34 int n, m, k;
 35 char str[inf][10];
 36 int app[inf];
 37 
 38 inline void connect(int from, int to){
 39     static edge pool[inf * 30];
 40     static edge* p = pool;
 41     p->to = to;
 42     p->nxt = g[from];
 43     g[from] = p; p++;
 44     return;
 45 }
 46 
 47 class Trie {
 48     private:
 49     struct node {
 50         int son[28];
 51         int id;
 52         node() {
 53             memset(son, -1, sizeof (son));
 54             id = -1;
 55         }
 56     };
 57 
 58     std::vector <node> t;
 59 
 60     inline int transInt(char c) {return (c == '_' ? 26 : c - 'a');}
 61 
 62     public:
 63     inline void insert(int x, int now, int id, char *s) {
 64         if(now == k + 1) {
 65             t[x].id = id;;
 66             return;
 67         }
 68         int nxt = transInt(s[now]);
 69         if(t[x].son[nxt] == -1) {
 70             t[x].son[nxt] = t.size();
 71             t.push_back(node());
 72         }
 73         insert(t[x].son[nxt], now + 1, id, s);
 74     }
 75 
 76     inline void query(int x, int now, int id, char *s) {
 77         if(now == k + 1) {
 78             if(t[x].id == id) app[id] = 1;
 79             else connect(id, t[x].id);
 80             return;
 81         }
 82         int nxt = transInt(s[now]);
 83         if(t[x].son[nxt] != -1) 
 84             query(t[x].son[nxt], now + 1, id, s);
 85         if(s[now] != '_' && t[x].son[transInt('_')] != -1)
 86             query(t[x].son[transInt('_')], now + 1, id, s);
 87         return;
 88     }
 89 
 90     Trie () {
 91         t.clear();
 92         t.push_back(node());
 93     }
 94 };
 95 
 96 Trie t;
 97 char s[inf][10];
 98 int mt[inf];
 99 std::vector <int> ord;
100 int vis[inf];
101 
102 inline void fail() {
103     puts("NO");
104     exit(0);
105 }
106 
107 void dfs(int x) {
108     vis[x] = 1;
109     for(auto e = g[x]; e; e = e->nxt) {
110         auto y = e->to;
111         if(vis[y] == 1) fail();
112         if(vis[y] == 0) dfs(y);
113     }
114     vis[x] = 2;
115     ord.push_back(x);
116 }
117 
118 inline void setting(){
119 #ifndef ONLINE_JUDGE
120     freopen("E.in", "r", stdin);
121     freopen("E.out", "w", stdout);
122 #endif
123     return;
124 }
125 
126 signed main(){
127     setting();
128     read(n, m, k);
129     for(int i = 1; i <= n; i++) scanf("%s", str[i] + 1);
130     for(int i = 1; i <= n; i++) t.insert(0, 1, i, str[i]);
131     for(int i = 1; i <= m; i++) scanf("%s", s[i] + 1), read(mt[i]);
132     for(int i = 1; i <= m; i++) {
133         t.query(0, 1, mt[i], s[i]);
134         if(app[mt[i]] == 0) fail();
135     }
136     for(int i = 1; i <= n; i++) {
137         if(vis[i] == 0) dfs(i);
138     }
139     puts("YES");
140     std::reverse(ord.begin(), ord.end());
141     for(auto i: ord) printf("%d ", i); puts("");
142     return 0;
143 }
复制代码

cf round700(div.2) C

容易想到二分,但是怎么二分呢?

考虑排列是 1,2,3,...,n,此时答案就是 1,最序列的最左侧,当排列非这样,则答案位置可能右移,但这个序列一定有答案

同样的,当序列是 n,..,3,2,1 时,可以与上面同理,答案在最右侧

那么二分时,查 mid,mid-1,mid+1,当 mid-1 的值小于 mid 位置时,可以递归到左侧,如果 mid+1 位置小于 mid 位置,可以递归到右侧

如此递归即可

复制代码
 1 #include <ctime>
 2 #include <cmath>
 3 #include <cctype>
 4 #include <cstdio>
 5 #include <cstring>
 6 #include <cstdlib>
 7 #include <iostream>
 8 #include <algorithm>
 9 #include <vector>
10 #include <queue>
11 #define inf 100010
12 #define INF 0x7fffffff
13 #define ll long long
14 
15 template <class I>
16 inline void read(I &num){
17     num = 0; char c = getchar(), up = c;
18     while(!isdigit(c)) up = c, c = getchar();
19     while(isdigit(c)) num = (num << 1) + (num << 3) + (c ^ '0'), c = getchar();
20     up == '-' ? num = -num : 0; return;
21 }
22 template <class I>
23 inline void read(I &a, I &b) {read(a); read(b);}
24 template <class I>
25 inline void read(I &a, I &b, I &c) {read(a); read(b); read(c);}
26 
27 int n;
28 int a[inf];
29 
30 inline bool check(int p) {
31     int a, b, c;
32     if(p - 1 < 1) a = INF;
33     else {
34         std::cout << "? " << p - 1 << std::endl;
35         std::cin >> a;
36     }
37     std::cout << "? " << p << std::endl;
38     std::cin >> b;
39     if(p + 1 > n) c = INF;
40     else {
41         std::cout << "? " << p + 1 << std::endl;
42         std::cin >> c;
43     }
44     if(a > b && b < c) {
45         std::cout << "! " << p << std::endl;
46         exit(0);
47     }
48     if(a < b) return 0;
49     return 1;
50 }
51 
52 signed main(){
53     std::cin >> n;
54     int l = 1, r = n + 1;
55     while(l < r - 1) {
56         int mid = (l + r) >> 1;
57         if(check(mid)) l = mid;
58         else r = mid;
59     }
60     std::cout << "! " << l << std::endl;
61     std::cout.flush();
62     return 0;
63 }
复制代码

 


CF round683(div.2) D

考虑 dp,设 dp_{i,j} 表示第一个串 i 位置的某个后缀,第二个串 j 位置的某个后缀的最优答案,那么考虑转移,如果当前位置字符是一样的,那么就是在原来基础上各加了一个位置,那么就是答案就要 +4-2 = +2,如果当前位置不同,那么考虑把两个串各往后移一位,那么就是不增加 LCS,但是长度 +1,那么答案就是 -1

复制代码
 1 #include <ctime>
 2 #include <cmath>
 3 #include <cctype>
 4 #include <cstdio>
 5 #include <cstring>
 6 #include <cstdlib>
 7 #include <iostream>
 8 #include <algorithm>
 9 #include <vector>
10 #include <queue>
11 #define inf 5010
12 #define INF 0x7fffffff
13 #define ll long long
14 
15 template <class I>
16 inline void read(I &num){
17     num = 0; char c = getchar(), up = c;
18     while(!isdigit(c)) up = c, c = getchar();
19     while(isdigit(c)) num = (num << 1) + (num << 3) + (c ^ '0'), c = getchar();
20     up == '-' ? num = -num : 0; return;
21 }
22 template <class I>
23 inline void read(I &a, I &b) {read(a); read(b);}
24 template <class I>
25 inline void read(I &a, I &b, I &c) {read(a); read(b); read(c);}
26 
27 int f[inf][inf];
28 int n, m;
29 char s[inf], t[inf];
30 
31 signed main(){
32     read(n, m);
33     std::cin >> (s + 1) >> (t + 1);
34     int ans = -INF;
35     for(int i = 1; i <= n; i++) {
36         for(int j = 1; j <= m; j++) {
37             if(s[i] == t[j]) f[i][j] = f[i - 1][j - 1] + 2;
38             else f[i][j] = std::max (f[i - 1][j], f[i][j - 1]) - 1;
39             f[i][j] = std::max (f[i][j], 0);
40             ans = std::max (ans, f[i][j]);
41         }
42     }
43     std::cout << ans << '\n';
44     return 0;
45 }
复制代码

 


cf round683(div.2) E

考虑一个好的序列无非是满足 n-1 条连边且全部联通,n-1 条连边无非是指存在且只存在一对数互相连边

我们可以发现一个性质,如果把 n 个数按照最高位是 0 还是 1 分成两个集合的话,那么显然两个集合间不会连边,除非有一个集合大小是 1

所以,答案只能是两个集合的最大答案 +1,这个 +1 就是加上了隔壁集合的一个元素

所以我们需要去找两个分集合的最大答案,递归即可

n 个元素,所以最短递归 30 \times n

复制代码
#include <ctime>
#include <cmath>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#define inf 200010
#define INF 0x7fffffff
#define ll long long

template <class I>
inline void read(I &num){
    num = 0; char c = getchar(), up = c;
    while(!isdigit(c)) up = c, c = getchar();
    while(isdigit(c)) num = (num << 1) + (num << 3) + (c ^ '0'), c = getchar();
    up == '-' ? num = -num : 0; return;
}
template <class I>
inline void read(I &a, I &b) {read(a); read(b);}
template <class I>
inline void read(I &a, I &b, I &c) {read(a); read(b); read(c);}

int n;
int nxt[inf * 30][2];

inline void getnxt(int x) {
    static int index = 0;
    int now = 0;
    for(int i = 30; i >= 0; i--) {
        bool c = x & (1 << i);
        if(nxt[now][c]) now = nxt[now][c];
        else now = nxt[now][c] = ++index;
    }
}

int dfs(int x) {
    if((nxt[x][0] || nxt[x][1]) == 0) return 1;
    if(nxt[x][0] == 0) return dfs(nxt[x][1]);
    else if(nxt[x][1] == 0) return dfs(nxt[x][0]);
    else return std::max (dfs(nxt[x][0]), dfs(nxt[x][1])) + 1;
}

signed main(){
    read(n);
    for(int i = 1; i <= n; i++) {
        int x; read(x);
        getnxt(x);
    }
    std::cout << (n - dfs(1)) << '\n';
    return 0;
}
复制代码

 


cf round666(div.2) D

luogu 你给我讲这是个黄题?

考虑最大一堆大于其他所有堆之和的情况,容易发现先手必胜

那么对于不满足这个条件的游戏状态,先手一定不愿意把当前状态转化成上述状态

所以轮流拿石子的结果就是这所有石子一定会被取到只有一堆,而这一堆一定只有一个

证明:设最后一堆剩了 {x}(x > 1) 个,则上一个状态一定是 {1,x} 个,为了防止下一个状态出现先手必胜态,当前玩家一定会选择拿走一个 x 堆的,递归成 {1,x-1},证毕

qc:打表容易得到答案

 


cf round707(div.2) C

震撼我妈的题,可以发现如果有多于或等于 2 个数出现过两次及以上,那么答案显然。对于其他情况,可以考虑相邻两两做差,a-c=d-b 就转化为找两段区间和相同的子区间,然后就死了,想各种分治做法,乱搞做法都暴毙了

然后发现就是个鸽笼原理傻逼题,数据范围 a_i\leq 2.5\times 10^6,那么两两之和最大为 5e6,所以直接暴力 O(n^2) 即可,最多枚举 5e6 次一定出答案

 abcd 可能相同,可以直接特判掉,复杂度变成 O(n^2+C),但是感觉或许能被卡死,于是各种特判,结果就是调了一小时才 A 掉

 


Edu82(div.2) D

二进制拆分,对于每一个 1,如果有一个正好对应他这个位置的物品,就直接用掉

对于没有对应物品的位置,我们首先希望有若干个比他小的物品凑出这个位置,那就直接向前遍历,否则我们需要拆大的物品,需要拆的次数就是二进制下位数差,可以证明拆离他最近的物品肯定最优

复制代码
#include <ctime>
#include <cmath>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#define inf 100007
#define N 100
#define P 63
#define INF 0x7fffffff
#define int long long
 
template <class I>
inline void read(I &num){
    num = 0; char c = getchar(), up = c;
    while(!isdigit(c)) up = c, c = getchar();
    while(isdigit(c)) num = (num << 1) + (num << 3) + (c ^ '0'), c = getchar();
    up == '-' ? num = -num : 0; return;
}
template <class I>
inline void read(I &a, I &b) {read(a); read(b);}
template <class I>
inline void read(I &a, I &b, I &c) {read(a); read(b); read(c);}
 
int n, m;
int a[inf];
int cnt[N];
int tmp[N];
 
inline void solve() {
    read(n, m);
    int ans = 0;
    memset(cnt, 0, sizeof(cnt));
    for(int i = 1; i <= m; i++) {
        read(a[i]);
        for(int j = 0; j <= P; j++) {
            if((1ll << j) & a[i]) {
                ++cnt[j];
                break;
            }
        }
    }
    for(int i = 0; i <= P; i++) {
        if((1ll << i) & n) {
            if(cnt[i]) --cnt[i];
            else {
                int goal = (1ll << i);
                memset(tmp, 0, sizeof (tmp));
                for(int j = i - 1; j >= 0; j--) {
                    while(cnt[j] && (1ll << j) <= goal) 
                        goal -= (1ll << j), --cnt[j], ++tmp[j];
                    if(goal == 0) goto success;
                }
                for(int j = i - 1; j >= 0; j--) cnt[j] += tmp[j], tmp[j] = 0;
                for(int j = i + 1; j <= P; j++) {
                    if(cnt[j]) {
                        --cnt[j];
                        ans += (j - i);
                        for(int k = i; k < j; k++) ++cnt[k];
                        goto success;
                    }
                }
                puts("-1");
                return;
                success: continue;
            }
        }
    }
    std::cout << ans << '\n';
}
 
signed main(){
    int T; read(T);
    while(T--) solve();
    return 0;
}
复制代码

 


cf round391(div.1+2) G

前置:树链剖分,可持久化线段树,标记永久化

容易发现 dist(u,v)=dep_u+dep_v-2dep_{LCA(u,v)}, 并有 \sum_{i=l}^rdist(i,p_i)=\sum_{i=1}^{r}dist(i,p_i)-\sum_{i=1}^{l-1}dist(i,p_i)

所以需要维护 \sum_{i=1}^ndep_u+\sum_{i=1}^ndep_{p_i}-2\times \sum_{i=1}^ndep_{LCA(u,p_i)}

第一项为常数,第二项前缀和维护,考虑维护第三项

考虑这样一个事,对于 1 到 n 的每一个 p_i,都把他到根的路径上的 cnt+=1,那么从点 u 到根路径上求 \sum_{i\in way} w_i\times cnt_i,就是 \sum_{i=1}^ndep_{LCA(u,p_i)}(设 w_i 为边权)

所以问题转化为路径加,路径求和,直接树链剖分

冷静一下,发现我们实际上需要做的是对一个点 u,求 n 时刻前,u 到 根 的路径和,所以我们树链剖分上实际套的数据结构是主席树

需要实现主席树区间加,标记永久化即可

代码实在有亿点难写,就贴一个标记永久化可持久化线段树做做样子吧(题面

复制代码
#include <ctime>
#include <cmath>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cassert>
#define inf 100010
#define INF 0x7fffffff
#define ll long long

template <class I>
inline void read(I &num){
    num = 0; char c = getchar(), up = c;
    while(!isdigit(c)) up = c, c = getchar();
    while(isdigit(c)) num = (num << 1) + (num << 3) + (c ^ '0'), c = getchar();
    up == '-' ? num = -num : 0; return;
}
template <class I>
inline void read(I &a, I &b) {read(a); read(b);}
template <class I>
inline void read(I &a, I &b, I &c) {read(a); read(b); read(c);}

int n, m;
int a[inf];
char opt[5];

int root[inf];
ll tag[inf << 4], sum[inf << 4];
int ls[inf << 4], rs[inf << 4];
int Index = -1;

void build(int l, int r, int &rt) {
    rt = ++Index;
    if(l == r) {
        sum[rt] = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, ls[rt]);
    build(mid + 1, r, rs[rt]);
    sum[rt] = sum[ls[rt]] + sum[rs[rt]];
    return;
}

ll query(int nl, int nr, int l, int r, int rt) {
    if(l <= nl && r >= nr) return sum[rt];
    int mid = (nl + nr) >> 1;
    ll chg = 1ll * tag[rt] * (std::min (nr, r) - std::max (nl, l) + 1);
    ll res = 0;
    if(l <= mid) res += query(nl, mid, l, r, ls[rt]);
    if(r > mid) res += query(mid + 1, nr, l, r, rs[rt]);
    return res + 1ll * (l <= mid || r > mid) * chg;
}

void modify(int nl, int nr, int l, int r, int k, int &rt, int nxt) {
    rt = ++Index;
    tag[rt] = tag[nxt];
    ls[rt] = ls[nxt], rs[rt] = rs[nxt];
    sum[rt] = sum[nxt] + 1ll * k * (std::min (nr, r) - std::max (nl, l) + 1);
    if(l <= nl && r >= nr) {
        tag[rt] += k;
        return;
    }
    int mid = (nl + nr) >> 1;
    if(l <= mid) modify(nl, mid, l, r, k, ls[rt], ls[nxt]);
    if(r > mid) modify(mid + 1, nr, l, r, k, rs[rt], rs[nxt]);
    return;
}

signed main(){
    read(n, m);
    for(int i = 1; i <= n; i++) read(a[i]);
    int now = 0;
    build(1, n, root[now]);
    while(m--) {
        std::cin >> opt;
        if(opt[0] == 'Q') {
            int l, r; read(l, r);
            printf("%lld\n", query(1, n, l, r, root[now]));
        } else if(opt[0] == 'C') {
            int l, r, k; read(l, r, k);
            ++now;
            modify(1, n, l, r, k, root[now], root[now - 1]);
        } else if(opt[0] == 'H') {
            int l, r, t; read(l, r, t);
            printf("%lld\n", query(1, n, l, r, root[t]));
        } else if(opt[0] == 'B') {
            int t; read(t);
            now = t, Index = root[t + 1] - 1;
        } else assert(0);
    }
    return 0;
}
复制代码

 

posted @   Chiaro  阅读(69)  评论(0)    收藏  举报
编辑推荐:
· C23和C++26的#embed嵌入资源指南
· 「EF Core」框架是如何识别实体类的属性和主键的
· 独立开发,这条路可行吗?
· 我在厂里搞 wine 的日子
· 如何通过向量化技术比较两段文本是否相似?
阅读排行:
· 他没买 iPad,而是花了半年时间,为所有“穷学生”写了个笔记神器
· Visual Studio 现已支持新的、更简洁的解决方案文件(slnx)格式
· 只需一行命令,Win11秒变Linux开发主机!
· 上周热点回顾(7.7-7.13)
· 也是出息了,业务代码里面也用上算法了。
点击右上角即可分享
微信分享提示