Codeforces Round 770 div.2 EF

E. Fair Share

题意:有 \(n\) 组物品,第 \(i\) 组有 \(m_i\) 个,每个物品有一个编号,保证每种物品出现偶数次,每组物品有偶数个,要求将所有物品划分为两组,使得一组中有每种物品各一半,每组物品各一半。

首先将问题抽象出来,组别和物品类别其实没有什么区别,都是物品的属性;

建图:左边点表示组别,右边点表示类别,\(i\) 组中包含物品 \(j\),则连边 \(i,j\)

因为保证了物品出现偶数次,一组也有偶数个物品,那么这张图必然存在若干条欧拉回路

寻找欧拉回路,对于每条欧拉回路,如果一条边是由左到右遍历,则分至 \(L\) 组,否则分至 \(R\) 组,由于欧拉回路的性质(一个点的入次数和出次数相等,这样一定能保证恰好均分)

然后我发现我好像从来没写过欧拉回路,想当然的写了一波TLE了一页(

关键在于,欧拉回路标记vis的是边而不是点,一个点可能遍历多次,如果还是每遍历到一个点就去遍历它的所有连边,即使有vis标记忽略已经走过的边,还是会变成 \(n^2\)

需要记录每个店最后一次遍历的边是哪条,下次再跑直接从这开始即可。

int n, m;
struct Edge{
    int x, y, pj, vis;
}e[maxn];
int ecnt = 0;
vector<int> vp[maxn];
int lst[maxn];
string ans[maxn];
 
void dfs(int p){
    for(int j=lst[p];j<vp[p].size();j=lst[p]){
        int i = vp[p][j];
        lst[p] = j+1;
        if(e[i].vis) continue;
        e[i].vis = 1;
        if(p <= n){
            ans[e[i].x][e[i].pj] = 'L';
            dfs(e[i].y);
        }else{
            ans[e[i].x][e[i].pj] = 'R';
            dfs(e[i].x);
        }
    }
}
 
void solve(){
    cin >> n;
    map<int, int> lsh;
    int tot = n;
    for(int i=1;i<=n;i++){
        cin >> m;
        ans[i] = string(m, '\0');
        for(int j=0;j<m;j++){
            int x; cin >> x;
            if(!lsh[x]) lsh[x] = ++tot;
            e[++ecnt] = {i, lsh[x], j, 0};
            vp[lsh[x]].push_back(ecnt);
            vp[i].push_back(ecnt);
        }
    };
    for(int i=1;i<=tot;i++){
        if(vp[i].size() % 2 != 0){
            cout << "NO\n";
            return;
        }
    }
    for(int i=1;i<=tot;i++) if(lst[i] < vp[i].size()) dfs(i);
    cout << "YES\n";
    for(int i=1;i<=n;i++) cout << ans[i] << '\n';
}

F. Fibonacci Additions

一道差分好题

题意:有长度为 \(n\)\(a\) 数组和 \(b\) 数组,接下来有若干次修改:使 \(a\) 或者 \(b\) 数组的 \([l,r]\) 段依次加上斐波那契数列,即 \(a_l+1\)\(a_{l+1}+1\)\(a_{l+3}+2\)...

每次修改完成后,要求输出 \(a\)\(b\) 是否完全相等。

首先需要利用斐波那契数列的性质:\(f_i=f_{i-1}+f_{i-2}\)

在空数组上随便修改几段试一下就会发现,不论怎么修改,在修改量上,永远有 \(d_i=d_{i-1}+d_{i-2}\)

既然如此,可以考虑对修改量差分(前缀和的形式改为斐波那契形式)

写个例子理解一下,比如要对3-6这段区间加斐波那契数列:

修改量:0 0 f1 f2 f3 f4 0 0 0

差分d: 0 0 f1 0 0 0 -f5 -f4 0

如果我们对差分数组这样修改,那么它的斐波那契前缀和即为修改量;

这样就简单啦,显然任意一组修改量和它的差分数组是一一对应的,我们直接把初始情况转化为差分数组,然后每次在差分数组上进行3次单点修改,就可以很方便的判断是否一样了。

ll n, q, mod, a[maxn], tot = 0;
ll f[maxn];
 
void modify(ll x, ll ad){
    if(x > n) return;
    if(a[x] == 0) tot--;
    a[x] = (a[x] + ad + mod) % mod;
    if(a[x] == 0) tot++;
}
 
void solve(){
    cin >> n >> q >> mod;
    f[1] = f[2] = 1;
    for(ll i=3;i<=n;i++) f[i] = (f[i-1] + f[i-2]) % mod;
    for(ll i=1;i<=n;i++) cin >> a[i];
    for(ll i=1;i<=n;i++){
        ll x; cin >> x;
        a[i] -= x;
        a[i] = (a[i] % mod + mod) % mod;
    }
    for(ll i=n;i>=2;i--) a[i] -= a[i-1] + a[i-2], a[i] = (a[i] % mod + mod) % mod;
    for(ll i=1;i<=n;i++) tot += !a[i];
 
    for(ll i=1;i<=q;i++){
        char c; ll x, y;
        cin >> c >> x >> y;
        if(c == 'A'){
            modify(x, 1);
            modify(y+2, -f[y-x+1]);
            modify(y+1, -f[y-x+2]);
        }else{
            modify(x, -1);
            modify(y+2, f[y-x+1]);
            modify(y+1, f[y-x+2]);
        }
        cout << vector<string>{"NO", "YES"}[tot == n] << '\n';
    }
}

Codeforces Round 770 div.2 EF
http://www.lxtyin.ac.cn/2022/02/06/题解/Codeforces Round 770 div.2 EF/
作者
lx_tyin
发布于
2022年2月6日
许可协议