CF1594

D - The Number of Imposters(2-sat)

每个人分成T,F两种结点,分别代表这人是诚实的还是不诚实的。然后连边,例如(a)(b)是T,那么就有如果(a)是T推出(b)是T,于是连一条双向边:(aT Leftrightarrow bT),以此类推。然后直接dfs跑,最后判一下有没有矛盾。

#include <bits/stdc++.h>

#define endl '
'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 1e6 + 10;
const double eps = 1e-5;

bool vis[N];
bool visid[N];
vector<int> np[N];

int get(int p, int st) { // 1 T 0 F
    if(st) return 2 * p - 1;
    return 2 * p;
}

void add(int u, int v) {
    np[u].push_back(v);
    np[v].push_back(u);
}
int cnt, fcnt;
void dfs(int p) {
    cnt++;
    fcnt += (p % 2 == 0);
    visid[(p + 1) / 2] = 1;
    vis[p] = 1;
    for(int nt : np[p]) {
        if(vis[nt]) continue;
        dfs(nt);
    }
}

int main() {
    IOS;
    int t;
    cin >> t;
    while(t--) {
        int n, m;
        cin >> n >> m;
        for(int i = 1; i <= 2 * n; i++) {
            vis[i] = 0;
            visid[i] = 0;
            np[i].clear();
        }
        while(m--) {
            int a, b;
            string c;
            cin >> a >> b >> c;
            bool flag = (c == "crewmate");
            add(get(a, 1), get(b, flag));
            add(get(a, 0), get(b, !flag));
        }
        int ans = 0;
        for(int i = 1; i <= 2 * n; i++) {
            if(visid[(i + 1) / 2]) continue;
            cnt = fcnt = 0;
            dfs(i);
            ans += max(cnt - fcnt, fcnt);
        }
        bool ok = true;
        for(int i = 1; i <= n; i++) {
            if(vis[get(i, 1)] && vis[get(i, 0)]) {
                ok = false;
                break;
            }
        }
        if(ok) cout << ans << endl;
        else cout << -1 << endl;
    }
}

E - Rubik's Cube Coloring (hard version) - (树上dp)

染色的结点只会影响从它到根结点的路径上的点,将所有染色结点以及它们到根结点的路径全部提出来,可以得到一颗树,点数最多为(60 imes 2000)。剩余的点显然都是只有4种染色方式,然后处理树上的染色方案数。直接树上dp,设(dp[p][c])代表结点(p)染色(c)时方案数。有转移方程

[dp[p][c]=sum{dp[u][c_1] cdot dp[v][c_2]} ]

(u,v)(p)的儿子,(c_1,c_2)是和(c)合法的颜色。

注意写前搞清楚dp转移方程,在纸上写下来。不要搞错了。

#include <bits/stdc++.h>

#define endl '
'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 3e5 + 10;
const int M = 1e9 + 7;
const double eps = 1e-5;
typedef pair<int, int> PII;
map<string, int > id;
vector<ll> cn[N];
vector<ll> num;
vector<int> np[N];
int qcol[N];
int col[N];
ll dp[N][10];
bool vis[N];

inline ll qpow(ll a, ll b, ll m) {
    ll res = 1;
    while(b) {
        if(b & 1) res = (res * a) % m;
        a = (a * a) % m;
        b = b >> 1;
    }
    return res;
}

int getid(ll x) {
    return lower_bound(num.begin(), num.end(), x) - num.begin() + 1;
}

void cal(int p, int fa, int c) {
    vector<int> son;
    for(int nt : np[p]) {
        if(nt == fa) continue;
        son.push_back(nt);
    }
    if(son.size() == 0) {
        dp[p][c] = 1;
    } else if(son.size() == 1) {
        int u = son.front();
        for(int i = 2; i <= 7; i++) {
            if(c == i || c == (i ^ 1)) continue;
            dp[p][c] += dp[u][i];
            dp[p][c] %= M;
        }
    } else {
        int u = son.front(), v = son.back();
        for(int i = 2; i <= 7; i++) {
            for(int j = 2; j <= 7; j++) {
                if(c == i || c == (i ^ 1)) continue;
                if(c == j || c == (j ^ 1)) continue;
                dp[p][c] += dp[u][i] * dp[v][j] % M;
                dp[p][c] %= M;
            }
        }
    }
}

void dfs(int p, int fa) {
    for(int nt : np[p]) {
        if(nt == fa) continue;
        dfs(nt, p);
    }
    if(col[p]) {
        cal(p, fa, col[p]);
    } else {
        for(int i = 2; i <= 7; i++) {
            cal(p, fa, i);
        }
    }
}

int main() {
    IOS;
    id["white"] = 2;
    id["yellow"] = 3;
    id["green"] = 4;
    id["blue"] = 5;
    id["red"] = 6;
    id["orange"] = 7;
    int k, n;
    cin >> k >> n;
    for(int i = 1; i <= n; i++) {
        ll v;
        string c;
        cin >> v >> c;
        while(v) {
            num.push_back(v);
            cn[i].push_back(v);
            v >>= 1;
        }
        qcol[i] = id[c];
    }
    sort(num.begin(), num.end());
    num.erase(unique(num.begin(), num.end()), num.end());
    for(int i = 1; i <= n; i++) {
        int u = getid(cn[i].front());
        col[u] = qcol[i];
        for(int j = 1; j < cn[i].size(); j++) {
            int v = getid(cn[i][j]);
            np[u].push_back(v);
            np[v].push_back(u);
            u = v;
        }
    }
    for(int i = 1; i <= num.size(); i++) {
        sort(np[i].begin(), np[i].end());
        np[i].erase(unique(np[i].begin(), np[i].end()), np[i].end());
    }
    dfs(1, 0);
    ll ans = 1;
    ll ret = ((1ll << k) - 1) - num.size();
    ll res = 0;
    for(int i = 2; i <= 7; i++) res = (res + dp[1][i]) % M;
    ans =  qpow(4, ret % (M - 1), M) * res % M;
    cout << ans << endl;
}

F - Ideal Farm (思维,构造)

如果(s < k),答案是NO ;(s=k)答案是YES

(s>k)时,设分配为(a_1,...a_n),有分配的前缀和为(p_1,...,p_n),其中(p_n = s)(p_i < p_{i+1})(p_1 ge 1)

这样转化后就比较好处理,假设存在某个(p_i),有(p_i +k=p_j),那么说明存在一个区间和为(k)。因此再创建一个数组(b),有(b_i=p_i+k)(b_0=k)。这样就有包含(n)个元素的(p)和包含(n+1)个元素的数组(b),一共(2n+1)个元素。

显然,答案为NO的充要条件就是存在一个分配,使得(p)中任意一个元素都不等于(b)中任意一个元素,等价于(p)(b)构成一个新数组(pb)后里面的元素各不相同。显然这(2n+1)个元素的值域为([1,s+k])。如果能找到(pb)的最长分配使得前面的条件成立,设最长的分配长度为(m),那么有(2n+1le m)答案为NO,否则答案为YES。因为一旦有最长的分配使得条件成立,让条件成立的更短的分配一定可以从中构造出来。

注意(p)的值域为([1,s])(b)的值域为([k,s+k])。最优(长)的分配方案就是(k)个一组,一组一组分别分配给(p)(b)。例如([2k,3k-1])分配给(p)([3k,4k-1])分配给(b),这样即满足各不相同又满足了(b_i=p_i+k)。可以发现,这样是成对分配的,所以要看(lfloor frac{s}{k} floor)是奇数还是偶数,如果是奇数,最后还会剩下一组。最后剩下的从小到大分配即可。注意,([s+1,s+k])这部分的值只能分配给(b)。画一下图很好理解。这样就能得到最长的分配长度。

#include <bits/stdc++.h>

#define endl '
'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 3e5 + 10;
const double eps = 1e-5;


int main() {
    IOS;
    int t;
    cin >> t;
    while(t--) {
        ll s, n, k;
        cin >> s >> n >> k;
        if(s < k) cout << "NO" << endl;
        else if(s == k) cout << "YES" << endl;
        else {
            ll b = s % k + 1;
            ll a = k - b;
            ll m;
            if((s / k) % 2 == 1) {
                m = s + k - b;
            } else {
                m = s + k - a;
            }
            if(2 * n + 1 <= m) {
                cout << "NO" << endl;
            } else 
                cout << "YES" << endl;
        }
    }
}
原文地址:https://www.cnblogs.com/limil/p/15387014.html