寒假思维训练计划day7 D Cyclic Operations

  • 宣传一下我自己对构造题的一些浅薄总结(附上例题):

 1、前后缀贪心,比如说观察前后缀的sum,去看以后怎么考虑最好。Problem - 1903C - Codeforces

2、双指针贪心法,考虑两端相消或者相互作用,还有就是考虑左右边界。   Problem - 1891C - Codeforces

Problem - 1907D - Codeforces

3、转换观察法,有些关系可以抽象成图,观察图的某些性质去总结规律。也可以抽象成一个集合,两个集合相等可以说明有解可构造。Problem - 1891C - Codeforces

4、打表找规律,一般没什么规律可循即可打表找规律,一般和数论有关的很喜欢考,acm也喜欢考,属于人类智慧题。Problem - 1916D - Codeforces

5、公式推导演算,常见的分为公式的等价变形、公式的化简(这个常考,一般需要先证明某些性质,可以直接抵消,一般如果原公式处理起来很复杂时就可以考虑)。Problem - 1889B - Codeforces

6、考虑奇偶数去简化问题或者分类问题,从其中的一些运算性质入手,因为奇数偶数的加减以及%运算(这个结论很重要)的结果的奇偶性是固定的,Problem - 1898C - Codeforces

7、根据性质构造模型,看看能不能分成几个块,几个不同的集合,再选择算法去解决。Problem - 1873G - Codeforces

8、考虑从小到大处理,或者是从大到小处理,有时候先处理小的对大的不会有影响,或者反过来,这样的处理顺序是最完美的。Problem - 1904D2 - Codeforces

9、边界贪心法,一般要在问题的最边界处考虑,有时候这样做结果是最优的,或者考虑边界上的影响,假如让影响最小,就使得影响<= 固定值 。 ​​​​​​Problem - E - Codeforces and Problem - 1903C - Codeforces


 day7(思维 + 推理 + 图论 + 并查集 + tarjan),模型:转换观察法): Problem - 1867D - Codeforces

  • 题意:
    给定长度为n的两个序列a, b和一个数字k,可以进行以下的操作:
    选定一个set L:[x,y,z...], length(L) = k, a[x] = y, a[y] = z, ... a[L[end]] = L[1],a最初都是0,问最后能不能构造成b,b[i] >= 1 and b[i] <= n
    题解:
    对于数组b再根据操作的性质,我们先对其建模,将b[i] -> b[b[i]],这样子其实构成了一张有向图,现在来推导以下图上的一些性质,首先是图上有环,说明可以互相推导,此时分类讨论:
    1、环长为k个节点,说明可以构造一个set推出这k个节点
    2、环长大于k个节点,说明无论如何都会有两个点是无法确认的,所以在这个环的某个边界上必须要指向环长为k的环的某个点,这样子可以进行传递到长度为k的环,先”牺牲“长度为k环上的点。
    3、和2同理。
    ​
    算法设计:
    因为环是强连通分量,所以我第一反应是用tarjan算法先处理好图,然后再用并查集和度数判断,如果同一个连通块中有长度为k的环且出度为0,那么这个连通块必然能推导出来。

代码:并查集 + tarjan, 代码很长,看solve()其实就可以了。

#include  
#define ff first 
#define ss second
#define pb push_back
using namespace std; 
using PII = pair; 
const int N = 2e5 + 10; 
int n, k; 
int a[N];
int h[N], e[N << 1], ne[N << 1], idx; 
int dout[N]; 
int dfn[N], low[N], timestamp; 
int stk[N], top; 
bool in_stk[N]; 
int id[N], scc_cnt, cnt[N]; 
int p[N];
void add(int a, int b) 
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++; 
}
void targan(int u) 
{
    dfn[u] = low[u] = ++ timestamp; 
    stk[++ top] = u, in_stk[u] = 1; 
    for(int i = h[u]; ~i; i = ne[i]) 
    {
        int j = e[i]; 
        if(!dfn[j]) 
        {
            targan(j); 
            low[u] = min(low[u], low[j]); 
        }
        else if(in_stk[j]) low[u] = min(low[u], low[j]); 
    }
    if(dfn[u] == low[u]) 
    {
        ++ scc_cnt; 
        int y; 
        do {
            y = stk[top --]; 
            in_stk[y] = 0; 
            id[y] = scc_cnt; 
            cnt[scc_cnt] ++; 
        }while(y != u); 
    }
}
int find(int x) {
    if(x != p[x]) p[x] = find(p[x]); 
    return p[x]; 
}
void solve() {
    idx = 0, timestamp = 0, top = 0, scc_cnt = 0;
    cin >> n >> k;
    for(int i = 1; i <= n; i ++ ) cin >> a[i]; 
    if(k == 1) {
        for(int i = 1; i <= n; i ++ ) {
            if(a[i] != i) {
                cout << "No" << endl;  
                return;
            }
        }
        cout << "Yes" << endl; 
        return;
    }
    for(int i = 0; i <= n; i ++ ) h[i] = -1, cnt[i] = 0, id[i] = 0, in_stk[i] = 0, dout[i] = 0, dfn[i] = 0,
    low[i] = 0;
    for(int i = 1; i <= n; i ++ ) 
        add(i,a[i]);
    for(int i = 1; i <= n; i ++ ) 
        if(!dfn[i]) targan(i);
    for(int i = 1; i <= scc_cnt; i ++ ) p[i] = i;
    for(int i = 1; i <= n; i ++ ) 
        for(int j = h[i]; ~j; j = ne[j]) {
            int x = e[j]; 
            if(id[x] == id[i]) continue;
            dout[id[i]] ++;
            if(find(id[i]) == find(id[x])) continue;
            p[find(id[x])] = find(id[i]); 
        }
    vector us[n + 1]; 
    vector cs;
    for(int i = 1; i <= scc_cnt; i ++ ) {
        us[find(i)].pb(i);
        if(p[i] == i) cs.pb(i);
    }
    bool flag = 1; 
    for(auto t1 : cs) {
        bool ck = 0; 
        for(auto t2 : us[t1]) {
            if(cnt[t2] == k && !dout[t2]) {
                ck = 1; 
                break; 
            }
        }
        if(!ck) {
            flag = 0;
            break; 
        }
    }
    if(flag) cout << "Yes" << endl; 
    else cout << "No" << endl; 
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); 
    cout.tie(0);
    int ts; 
    cin >> ts; 
    while(ts --) solve(); 
    return 0; 
}

你可能感兴趣的:(算法)