CF603题解

CF603

Codeforces Round 334 (Div. 1)

CF603A

link

CF603A题意

现有一个长度为 \(n\) 的 01 串, 可以进行一次区间翻转 ( 起点终点随意, 并且区间里的值 1 变 0, 0 变 1 ), 得到一个新的 01 串, 使得得到的新的 01 串中的一个子串最长 (子串不一定连续), 且这个子串是 01 间隔的,没有连续两个字符相同。比如 0101 , 1011010 是合法的, 100, 1100 是不合法的。试求翻转后最长 01 间隔子串的长度。

CF603A题解

\(f_{i,0/1/2}\) 分别表示 \(i\) 没有反转,\(i\) 前面没有反转区间 / \(i\) 没有反转,\(i\) 前面有反转区间 / \(i\) 反转,\(i\) 前面有反转区间 的情况下最长 \(01\) 子串。

那么显然有

\[f_{i,0}=f_{i-1,0}+[a_i\not= a_{i-1}]\\ f_{i,1}=\max{f_{i-1,[a_i=a_{i-1}] + 1}+1,f_{i-1,[a_i\neq a_{i-1}]+1}}\\ f_{i,2}=\max{f_{i-1,2}+[a_i\neq a_{i-1}],f_{i-1,0}+[a_i=a_{i-1}]} \]

没了。

CF603A代码

#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;
char ch[maxn];
int f[maxn][3];
signed main(){
    n = read();scanf("%s",ch + 1);
    int ans = 1;
    f[1][0] = f[1][1] = f[1][2] = 1;
    for(int i = 2;i <= n;i++){
        f[i][0] = f[i - 1][0] + (ch[i] != ch[i - 1]);
        f[i][1] = max(f[i - 1][1 + (ch[i] == ch[i - 1])] + 1,f[i - 1][1 + (ch[i] != ch[i - 1])]);
        f[i][2] = max(f[i - 1][2] + (ch[i] != ch[i - 1]),f[i - 1][0] + (ch[i] == ch[i - 1]));
        ans = max(max(ans,f[i][0]),max(f[i][1],f[i][2]));
    }
    printf("%d\n",ans);
    return 0;
}

CF603B

link

CF603B题意

已知:\(f(k\times x\mod p)==k\times f(x)\mod p\)

对于一个从 \(f\{0,1,2,\dots,p-1\}\to\{0,1,2,\dots,p-1\}\) 的映射,问
对于给定的p和k,有多少种满足已知等式的映射

答案对 \(10^9+7\) 取模。

CF603B题解

下文中设 \(g(x)=k\times x \mod p\)
\(k=0\) 时,\(\forall x\in[0,p-1],g(x)=0\),所以除了 \(f(0)=0\) 之外,剩下的随便,答案是 \(p^{p-1}\)
\(k=1\) 时,\(\forall x\in[0,p-1],g(x)=x\),所以随便映射,答案是 \(p^{p}\)
考虑完两种特殊情况之后,我们找到最小的正整数 \(m\),使得 \(k^m\equiv 1\pmod p\)
不难发现,这个 \(m\)\([1,p-1]\) 分成了 \(\frac{p-1}{m}\) 个环,这些环可以互相映射,答案是 \(p^{\frac{p-1}{m}}\)
没了。
sbEric高一都学什么了

CF603B代码

#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, mod = 1e9 + 7;
int p, k;
int qpow(int x,int a){
    int res = 1;
    while(a){
        if(a & 1)res = res * x % mod;
        x = x * x % mod;a >>= 1;
    }
    return res;
}
signed main(){
    p = read(); k = read();
    if(k == 0){printf("%lld\n",qpow(p,p - 1));return 0;}
    if(k == 1){printf("%lld\n",qpow(p,p));return 0;}
    int m = 1,tmp = k;while(tmp != 1){tmp = tmp * k % p;m++;}
    printf("%lld\n",qpow(p,(p - 1) / m));
    return 0;
}

CF603C

link

CF603C题意

有两个人做游戏,游戏规则如下:

有n堆石子,每次可以对一堆石子进行操作,如果当前石子是偶数,那么可以选择将这2*x个石子分成k堆石子数为x的石子堆,还有一种没有前提的操作是取走当前堆的一个石子,问先手赢还是后手赢,先手和后手都足够聪明的情况下。

先手赢输出Kevin,后手赢输出Nicky

CF603C题解

尝试设定SG函数。
\(0\)\(4\)SG函数是可以手算的。
现在需要算 \(SG(x),x\ge 5\)
考虑对于 \(k\)\(x\) 的奇偶性分类讨论。

  • \(k\) 是奇数,\(x\) 是奇数:\(SG(x)=mex\{SG(x-1)\}=0\)
  • \(k\) 是奇数,\(x\) 是偶数:\(SG(x)=mex\{SG(\frac{x}{2}),SG(x-1)\}=mex\{SG(\frac{x}{2}),0\}\),而 \(SG(\frac{x}{2})\) 进行递归计算,次数不会超过 \(\log x\) 次。
  • \(k\) 是偶数,\(x\) 是偶数:\(SG(x)=mex\{SG(x-1),SG(\frac{x}{2}) \oplus SG(\frac{x}{2})=0\}=1\)
  • \(k\) 是偶数,\(x\) 是奇数:\(SG(x)=mex\{SG(x-1)\}=0\)

没了。

CF603C代码

#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, k;
int SG(int x){
    if(x <= 1)return x;
    if(k % 2 == 1){
        if(x <= 4)return x - 2;
        if(x & 1)return 0;
        return SG(x / 2) != 1 ? 1 : 2;
    }
    if(x == 2)return x;
    return x % 2 == 0;
}
signed main(){
    n = read(); k = read();int ans = 0;
    for(int i = 1;i <= n;i++)ans ^= SG(read());
    puts(ans ? "Kevin" : "Nicky");
    return 0;
}

CF603D

link

CF603D题意

\(n\) 条直线,两两不平行,任意 \(3\) 条直线不经过同一个点。求三条直线 \(i,j,k\) 围成的三角形的外接圆过原点的三元无序点 \((i,j,k)\) 的数量。
\(n\le2000\)

CF603D题解

阅读前请确保你知道什么是Simson_line
对于每条线段都求出原点到这条直线的垂点。
然后这个问题就转化成为了求三点共线的无序点对 \((i,j,k)\) 的数量。
这东西想怎么求怎么求,反正 \(O(n^2)\sim O(n^2\log n)\) 都是可过的。
不过必须注意的是,这题TM卡double的精度,让 \(eps\gets 10^{-10}\) 的精度不够,需要 \(10^{-12}\),大了小了都不行。

CF603D代码

#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 = 2e3 + 10;
const double eps = 1e-12;
int a[maxn], b[maxn], c[maxn];
int n;
double x[maxn], y[maxn];
signed main(){
    n = read();
    for(int i = 1;i <= n;i++){
        a[i] = read(); b[i] = read(); c[i] = read();
        x[i] = 1.0 * c[i] * a[i] / (a[i] * a[i] * 1.0 + b[i] * b[i] * 1.0);
        y[i] = 1.0 * c[i] * b[i] / (a[i] * a[i] * 1.0 + b[i] * b[i] * 1.0);
        // printf("x = %lf, y = %lf\n",x[i],y[i]);
    }
    int ans = 0;
    for(int i = 1;i <= n;i++){
        int sam = 0,cnt = 0;vector<double> vec;vec.clear();
        for(int j = 1;j <= n;j++){
            if(j == i)continue;
            double dx = x[j] - x[i];
            double dy = y[j] - y[i];
            if(abs(dx) < eps && abs(dy) < eps)sam++;
            else if(abs(dx) < eps) cnt++;
            else vec.push_back(dy / dx);
        }
        sort(vec.begin(),vec.end(),greater<double>());
        int k = 0;
        ans += (cnt - 1 + sam) * (cnt + sam);
        for(int j = 0;j < vec.size();j += k, k = 1){
            while(j + k < vec.size() && abs(vec[k + j] - vec[j]) < eps)k++;
            ans += (k + sam) * (k + sam - 1);
            // printf("j = %d k = %d\n",j,k);
        }
        // for(int j = 1;j < vec.size();j++)
        //     printf("j = %d,dirt = %lf\n",j,vec[j + 1] - vec[j]);
    }
    printf("%d\n",ans / 6);
    return 0;
}

CF603E

link

CF603E题意

给定一张 \(n\) 个点的无向图,初始没有边。

依次加入 \(m\) 条带权的边,每次加入后询问是否存在一个边集,满足每个点的度数均为奇数。

若存在,则还需要最小化边集中的最大边权。

\(n \le 10^5\)\(m \le 3 \times 10^5\)

CF603E题解

首先想想这个条件:要求每个点的度数都是奇数。
其实这个限制已经很强了:

  • 对于一个成立的图,一定不能有一个大小为奇数的联通块。
  • 简单证明:每加入一条边,都会改变两个点的度数奇偶性,最开始每个点都是偶数的度数,奇数个点显然不能全都改变成奇数的度数。
  • 那联通块大小是偶数就一定都成立吗?
  • 一定,你先搞一个联通块的任意生成树,然后从下向上,一旦有的点度数不是奇数,那么就删除向 \(fa_u\) 连接的边,这样的话,只有根有可能是不满足的。
  • 但是显然是满足的,如果不满足,那么整个联通块的度数之和就是奇数,显然不可能出现这种情况。

于是问题就变成了,每次加入一条边,要求你选择一些边,使得所有的联通块都是偶数的大小,并且希望连接的边最大值最小。

不难联想到Kruskal最小瓶颈生成树。

想到线段树分治,从右向左维护答案。
于是问题就变成了,一条边在哪个时间段会有贡献。
我们知道,如果有一条边在某一时刻没有被选择加入最小生成树,那么任意时刻都不会再选择它了。
于是维护Kruskal加入边时的指针,每次更新直到满足条件或者没有边可用。
然后如果一条边被加入,那么就在 \([id,l - 1]\) 这个时间段加入这条边即可。

CF603E代码

#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, maxm = 3e5 + 10;
int n, m;

struct edge{
    int fr, to ,wei, id;
    edge(int fr = 0,int to = 0,int wei = 0,int id = 0):fr(fr),to(to),wei(wei),id(id){}
    friend bool operator < ( edge a,edge b){return a.wei < b.wei;}
}e[maxm];

struct mystk{
    struct node{
        int x, y, dep, updodd, siz;
        node(int x = 0,int y = 0,int dep = 0,int siz = 0,int updodd = 0):x(x),y(y),dep(dep),updodd(updodd),siz(siz){}
    };
    node st[maxm];
    int op;
    void pop(){op--;}
    node top(){return st[op];}
    void push(node x){st[++op] = x;}
    bool empty(){return op == 0;}
};
struct DSU{
    mystk stk;
    int oddblock;
    int fa[maxn], dep[maxn], siz[maxn];
    void init(int x = 0){oddblock = x;for(int i = 1;i <= x;i++){fa[i] = i;dep[i] = siz[i] = 1;}}
    int getf(int x){return fa[x] == x ? x : getf(fa[x]);}
    void merge(int x,int y){
        int fx = getf(x), fy = getf(y);
        // printf("getf(%d) = %d,getf(%d) = %d\n",x,fx,y,fy);
        if(fx == fy)return;
        // printf("merged %d, %d\n",x,y);
        if(dep[fx] > dep[fy])swap(fx,fy);
        stk.push(mystk::node(fx,fy,dep[fx] == dep[fy],siz[fx],((siz[fx] + siz[fy]) % 2 == 1) - ((siz[fx] % 2 == 1) + (siz[fy] % 2 == 1))));
        oddblock += (((siz[fx] + siz[fy]) % 2 == 1) - ((siz[fx] % 2 == 1) + (siz[fy] % 2 == 1)));
        fa[fx] = fy;dep[fy] += (dep[fx] == dep[fy]); siz[fy] += siz[fx];
    }
    void trace(int rev = -1){
        while(rev < stk.op){
            // printf("top = %d,rev=  %d\n",stk.op,rev);
            int x = stk.top().x, y = stk.top().y, dept = stk.top().dep, sz = stk.top().siz, updodd = stk.top().updodd;
            fa[x] = x;dep[y] -= dept;siz[x] = sz;siz[y] -= sz;oddblock -= updodd; stk.pop();
        }
    }
}dsu;
int pos, ans[maxm];
struct Segment_Tree{
    vector<int> d[maxm << 2];
    void update(int l,int r,int s,int t,int p,int id){
        if(s <= l && r <= t){d[p].push_back(id);return;}
        int mid = l + r >> 1;
        if(s <= mid)update(l,mid,s,t,p << 1,id);
        if(mid < t)update(mid + 1,r,s,t,p << 1 | 1,id);
    }
    void update(int s,int t,int id){update(1,m,s,t,1,id);}
    void query(int l,int r,int p){
        int ver = dsu.stk.op;
        for(int i : d[p])dsu.merge(e[i].fr,e[i].to);
        int mid = l + r >> 1;
        if(l == r){
            while(dsu.oddblock != 0 && pos < m){
                pos++;
                // printf("in rev = %d, l = %d, pos = %d\n",ver, l, pos);
                // printf("u = %d v = %d w = %d\n",e[pos].fr,e[pos].to,e[pos].wei);
                if(e[pos].id <= l){
                    dsu.merge(e[pos].fr,e[pos].to);
                    if(l != 1) update(e[pos].id,l - 1,pos);
                }
                // printf("odd = %d\n",dsu.oddblock);
            }
            if(dsu.oddblock == 0)ans[l] = e[pos].wei;
            else ans[l] = -1;
        }
        else {query(mid + 1,r,p << 1 | 1);query(l,mid,p << 1);}
        dsu.trace(ver);
    }
}tree;

signed main(){
    n = read(); m = read();int u, v, w;
    if(n & 1){for(int i = 1;i <= m;i++)puts("-1");return 0;}
    for(int i = 1;i <= m;i++){
        u =  read(); v=  read(); w = read();
        e[i] = edge(u, v, w, i);
    }
    sort(e + 1,e + 1 + m);dsu.init(n); tree.query(1, m, 1);
    for(int i = 1;i <= m;i++)printf("%d\n",ans[i]);
    return 0;
}

posted @ 2023-12-06 10:12  Call_me_Eric  阅读(139)  评论(0)    收藏  举报
Live2D