• 2020 CCPC-Wannafly Winter Camp Day2


    PTA
    牛客

    A. 托米的字符串

    显然答案为:

    [frac{n(n+1)}{2}cdotsum_{len=1}^nfrac{f(len)}{len} ]

    其中(f(i))表示长度为(i)的所有串中含元音的个数。
    显然(f(1))易求,那么(displaystyle f(2)=f(1)+sum_{i=2}^{n-1}[s_iin{a,e,i,o,u,y}],f(3)=f(2)+sum_{i=3}^{n-2}),同理可递推出其余的(f)。然后代入上式求解即可。
    当然也可以直接分析每个原因对每个长度的贡献,最终发现贡献呈现“梯形”:即先上升,然后持平,然后下降。
    因为贡献的变化是连续的,所以可以直接作两次前缀和即可得出答案。
    代码如下:

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2020/1/13 13:30:23
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 1e6 + 5;
    
    char s[N];
    
    bool ok(char c) {
        return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || c == 'y';
    }
    
    ll sum[N];
    
    void run(){
        cin >> (s + 1);
        int n = strlen(s + 1);
        for(int i = 1; i <= n; i++) if(ok(s[i])) {   
            int Min = min(i, n - i + 1), Max = max(i, n - i + 1);
            ++sum[1];
            --sum[Min + 1];
            --sum[Max + 1];
        }
        for(int i = 1; i <= n; i++) sum[i] += sum[i - 1];
        for(int i = 1; i <= n; i++) sum[i] += sum[i - 1];
        long double ans = 0;
        for(int i = 1; i <= n; i++) ans += (long double)1.0 * sum[i] / i;
        ans /= (long double)1.0 * n * (n + 1) / 2;
        printf("%.10Lf", ans);
    }
    
    int main() {
        run();
        return 0;
    }
    

    B. 萨博的方程式

    题意大致为求解一个含有(n)个变量,每个变量有上限的一个异或方程。
    容易想到,我们按二进制位从高到低来考虑。
    假设考虑二进制位(k)作为最高位的情况,如果(x_i)此位为(1),就有两种情况:一种是这位取(1)的时候,则后面的取值有((x_i&((1<<k)-1)))种情况;这一位取(0)时,后面的取值有((1<<k))种情况;如果(x_i)此时为(0),那么后面就有((x_i&((1<<k)-1)))种情况。
    那么我们用背包的思想(dp)出该位取值的所有情况。
    现有一个观察:就是当最高位为(1),但取值为(0)时,后面可以任意选,那么对于其它(x)无论取哪种值,最后都可以存在一个合法解。
    所以若最高位不全为(1)时,我们除以(2^k)即可得到当前这位的方案数;若全为(1)时,以(k-1)为最高位继续搞即可。

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2020/2/9 9:23:17
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 50 + 5, MOD = 1e9 + 7;
    
    int n, K;
    int a[N], f[N][N];
    
    int qpow(ll a, ll b) {
        ll res = 1;
        while(b) {
            if(b & 1) res = res * a % MOD;
            a = a * a % MOD;
            b >>= 1;   
        }
        return res;   
    }
    
    int solve(int k) {
        if(k < 0) return 1;
        memset(f, 0, sizeof(f));
        f[0][0] = 1;
        int t = 0, ans = 0;
        for(int i = 1; i <= n; i++) {
            if(a[i] >> k & 1) {
                ++t;
                for(int j = 0; j <= t; j++) {
                    if(j) f[t][j] = 1ll * f[t - 1][j - 1] * ((a[i] & ((1 << k) - 1)) + 1) % MOD;
                    f[t][j] = (f[t][j] + 1ll * f[t - 1][j] * (1 << k) % MOD) % MOD;
                }
            } else {
                for(int j = 0; j <= t; j++) {
                    f[t][j] = 1ll * f[t][j] * ((a[i] & ((1 << k) - 1)) + 1) % MOD;
                }
            }
        }
        int i, inv = qpow(1 << k, MOD - 2);
        for(i = (K >> k) & 1; i < t; i += 2) {
            ans += 1ll * inv * f[t][i] % MOD;
            if(ans >= MOD) ans %= MOD;
        }
        if(i == t) ans = (ans + solve(k - 1)) % MOD;
        return ans;
    }
    
    void run(){
        for(int i = 1; i <= n; i++) cin >> a[i];
        cout << solve(30) << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        while(cin >> n >> K) run();
        return 0;
    }
    

    C. 纳新一百的石子游戏

    假设当前异或和为(x),显然我们要寻找某个位置(y),满足(y xor x<y)
    假设(x)的二进制最高位为(k),那么所有二进制位为(k),此时必有(y xor x<y)
    代码如下:

    Code
    #include<bits/stdc++.h>
    typedef long long ll;
    typedef unsigned long long ull;
    typedef double db;
    const int MAXN = 1e5+5,MAXM = 1e6+5,MOD = 998244353,INF = 0x3f3f3f3f,N=2e5;
    const ll INFL = 0x3f3f3f3f3f3f3f3f;
    const db eps = 1e-7;
    #define lson o<<1,l,m
    #define rson o<<1|1,m+1,r
    #define mid l + ((r-l)>>1)
    #define pii pair<int,int>
    #define vii vector<pii>
    #define vi vector<int>
    #define x first
    #define y second
    using namespace std;
    
    int n,cnt[66];
    ll a[MAXN];
    int main(){
        ios::sync_with_stdio(false);cin.tie(0);
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        ll x=0;
        for(int i=1;i<=n;i++){
            for(int j=0;j<60;j++){
                if(a[i]>>j&1)cnt[j]++;
            }
            x^=a[i];
            int ans=0;
            for(int j=59;j>=0;j--){
                if(x>>j&1){
                    ans += cnt[j];
                    break;
                }
            }
            cout<<ans<<'
    ';
        }
        return 0;
    }
    

    D. 卡拉巴什的字符串

    题目即是要求对于每个前缀的所有后缀的(lcp)长度的(mex)值。
    假设最长的(lcp)长度为(x),那么(0)~(x)所有的值都存在,答案即为(x+1)。当然要注意特判(0)的情况:当字符串所有字符都相同时,(0)的情况不会出现。
    那么直接考虑后缀自动机(|endpos|>1)的所有结点,将这些结点的(len)(max)即可。
    当然,深度越大的结点(除开只包含单个位置的结点)他们的(len)值也最大,所以也可以直接找(np)的父亲结点取(max)即可。
    两种的时间复杂度都为(O(n))

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2020/2/8 16:35:25
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 1e6 + 5;
    
    int n;
    char s[N];
    
    struct node{
        int ch[26];
        int len, fa;
        node(){memset(ch, 0, sizeof(ch)), len = 0;}
    }dian[N << 1];
    int last, tot, cnt;
    bool chk[N];
    void init() {
        last = tot = 1;
        cnt = dian[1].len = 0;
        for(int i = 1; i <= 2 * n; i++) memset(dian[i].ch, 0, sizeof(dian[i].ch));
    }
    void add(int c) {
        int p = last;
        int np = last = ++tot;
        dian[np].len = dian[p].len + 1;
        for(; p && !dian[p].ch[c]; p = dian[p].fa) dian[p].ch[c] = np;
        if(!p) {
            dian[np].fa = 1;
            ++cnt;
            if(cnt == 2) chk[0] = true;
        }
        else {
            int q = dian[p].ch[c];
            if(dian[q].len == dian[p].len + 1) dian[np].fa = q, chk[dian[q].len] = true;
            else {
                int nq = ++tot; dian[nq] = dian[q];
                dian[nq].len = dian[p].len + 1;
                dian[q].fa = dian[np].fa = nq;
                chk[dian[nq].len] = true;
                for(; p && dian[p].ch[c] == q; p = dian[p].fa) dian[p].ch[c] = nq;
            }
        }
    }
    
    void run(){
        cin >> (s + 1);
        n = strlen(s + 1);
        for(int i = 0; i <= n + 1; i++) chk[i] = false;
        init();
        int ans = 0;
        for(int i = 1; i <= n; i++) {
            add(s[i] - 'a');   
            while(chk[ans]) ++ans;
            cout << ans << " 
    "[i == n];
        }
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        int T; cin >> T;
        while(T--) run();
        return 0;
    }
    

    E. 阔力梯的树

    直接启发式合并即可,复杂度(O(nlog^2n))
    一开始用(set)空间复杂度消耗太大了,后来直接用的(map),问题不大。

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2020/1/13 14:48:19
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 1e5 + 5;
    
    map <int, int> s[N];
    
    int n;
    int sz[N];
    vector <int> g[N];
    ll ans[N];
    
    void dfs(int u) {
        for(auto v : g[u]) {
            dfs(v);
            sz[u] += sz[v];
        }   
    }
    
    //merge y to x
    void merge(int x, int y) { 
        for(auto it : s[y]) {
            if(it.fi < 0 || it.fi == INF) continue;
            auto p = s[x].lower_bound(it.fi);
            auto p2 = p; --p2;
            if(p2 -> fi <= 0) {
                ans[x] += 1ll * (p -> fi - it.fi) * (p -> fi - it.fi);
            } else if(p -> fi >= INF) {
                ans[x] += 1ll * (p2 -> fi - it.fi) * (p2 -> fi - it.fi);
            } else {
                ans[x] -= 1ll * (p -> fi - p2 -> fi) * (p -> fi - p2 -> fi);
                ans[x] += 1ll * (p -> fi - it.fi) * (p -> fi - it.fi) + 1ll * (p2 -> fi - it.fi) * (p2 -> fi - it.fi);
            }
            s[x][it.fi] = 1;
        }   
    }
    
    void dfs2(int u) {
        int bson = -1, Max = 0;
        for(auto v : g[u]) {
            if(sz[v] > Max) {
                Max = sz[v];
                bson = v;   
            }
            dfs2(v);
        }
        if(bson != -1) {
            ll t = ans[bson];
            merge(bson, u);
            s[u].swap(s[bson]);
            swap(ans[u], ans[bson]);
            ans[bson] = t;
        }
        //dbg(u, ans[u]);
        //for(auto it = s[u].begin(); it != s[u].end(); ++it) {
            //dbg(it -> fi);   
        //}
        for(auto v : g[u]) if(v != bson) {
            merge(u, v);   
        }
    }
    
    void run(){
        for(int i = 2; i <= n; i++) {
            int p; cin >> p;
            g[p].push_back(i);   
        }
        for(int i = 1; i <= n; i++) {
            s[i][i] = 1;
            s[i][-INF] = 1;
            s[i][INF] = 1;
            sz[i] = 1;
        }
        dfs(1);
        dfs2(1);
        for(int i = 1; i <= n; i++) cout << ans[i] << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        while(cin >> n) run();
        return 0;
    }
    

    F. 采蘑菇的克拉莉丝

    考虑暴力的做法,对于不同的起点,回答每个询问时,枚举起点及其所有出边,然后对于每颗子树利用(dfs)序+树状数组计算答案即可。
    但这样时间复杂度为(O(n^2))的,接下来考虑优化。
    我们枚举出边时,只枚举重儿子的边和往父亲的边,只剩下轻边的贡献没统计。
    在进行(add)操作时,我们直接从每个结点往上面跳,因为轻重链的个数不会超过(O(logn)),在轻边时往父亲结点打上标记,提前算好贡献即可。
    时间复杂度为(O(nlogn))
    代码如下:

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2020/2/8 8:47:20
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 1e6 + 5;
    
    int n, q;
    struct Edge {
        int v, w, next;   
    }e[N << 1];
    int head[N], tot;
    void adde(int u, int v, int w) {
        e[tot].v = v; e[tot].w = w; e[tot].next = head[u]; head[u] = tot++;   
    }
    
    int dfn, T;
    int l[N], r[N], fa[N], w[N];
    int bson[N], sz[N], top[N];
    
    void dfs(int u, int f, int v) {
        int Max = 0;
        sz[u] = 1;
        l[u] = ++dfn;
        fa[u] = f, w[u] = v;
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(v == f) continue;
            dfs(v, u, e[i].w);   
            if(sz[v] > Max) {
                bson[u] = v;
                Max = sz[v];   
            }
            sz[u] += sz[v];
        }
        r[u] = dfn;
    }
    
    ll c[N];
    int lowbit(int x) {return x & (-x);}
    void add(int x, int v) {
        for(; x <= n; x += lowbit(x)) c[x] += v;
    }
    ll query(int x) {
        ll res = 0;
        for(; x > 0; x -= lowbit(x)) res += c[x];
        return res;
    }
    ll query(int L, int R) {
        return query(R) - query(L - 1);   
    }
    
    void dfs2(int u, int f, int topf) {
        top[u] = topf;
        if(bson[u] != 0) {
            dfs2(bson[u], u, topf);   
        }
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(v != bson[u] && v != f) dfs2(v, u, v);   
        }
    }
    
    ll ans[N];
    
    void run(){
        memset(head, -1, sizeof(head)), tot = 0;
        cin >> n;
        for(int i = 1; i < n; i++) {
            int u, v, w; cin >> u >> v >> w;
            adde(u, v, w); adde(v, u, w);
        }
        dfs(1, 0, 0);
        dfs2(1, 0, 1);
        cin >> q;
        ll t = 0;
        int s = 1;
        while(q--) {
            int op; cin >> op;
            if(op == 1) {
                int v, x; cin >> v >> x;
                t += x;
                add(l[v], x);
                while(top[v] != 1) {
                    ans[fa[top[v]]] += 1ll * w[top[v]] * x;
                    v = fa[top[v]];
                }
            } else {
                int v; cin >> v;
                s = v;
            }
            ll res = ans[s];
            res += 1ll * w[bson[s]] * query(l[bson[s]], r[bson[s]]);
            res += 1ll * w[s] * (t - query(l[s], r[s]));
            cout << res << '
    ';
        }
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
        return 0;
    }
    

    H. 叁佰爱抠的序列

    考虑(k)个点的完全图,若(k)为奇数,那么所有点的度数为偶数,则存在一条欧拉回路;若(k)为偶数,那么将结点两两配对连边,最终所有点的度数也为偶数。
    因此,在通过二分/贪心/小心枚举算出(m)过后,直接建图跑欧拉回路即可。
    细节可能有点点多。

    Code
    #include<bits/stdc++.h>
    #define REP(i,a,b) for(int i=(a); i<(b); i++)
    #define REPE(i,a,b) for(int i=(a); i<=(b); i++)
    #define PERE(i,a,b) for(int i=(a); i>=(b); i--)
    using namespace std;
    typedef long long ll;
    ll calceven(ll x) {
    	return ((ll)sqrtl((long double)(x*2)));
    }
    ll calcodd(ll x) {
    	return (1+(ll)sqrtl((long double)(x*8-7)))/2;
    }
    #define MAXN 4000007
    struct bian {
    	int v, nxt;
    };
    bian e[MAXN];
    int hd[MAXN], tot;
    bool vis[MAXN];
    void init() {
    	memset(hd,-1,sizeof hd);
    	memset(vis,0,sizeof vis);
    	tot=0;
    }
    void adde(int u, int v) {
    	e[tot]=(bian){v,hd[u]};
    	hd[u]=tot++;
    	e[tot]=(bian){u,hd[v]};
    	hd[v]=tot++;
    }
    int ans[MAXN], ansn;
    void go(int u) {
    	for(int now=hd[u]; ~now; now=e[now].nxt) {
    		if(!vis[now>>1]) {
    			vis[now>>1]=1;
    			go(e[now].v);
    			ans[ansn++]=e[now].v+1;
    		}
    	}
    }
    int main() {
    	ll n;
    	scanf("%lld", &n);
    	if(n==2) {puts("2
    1 2"); return 0;}
    	if(n==3) {puts("2
    1 2 1"); return 0;}
    	ll m1=calceven(n), m2=calcodd(n);
    	if(m1&1) m1--;
    	if(!(m2&1)) m2--;
    	if(m1<3) m1=m2;
    	else m1=max(m1,m2);
    	init();
    	printf("%lld
    ", m1);
    	if(n>2000000) return 0;
    	if(m1&1) { //odd
    		REP(i,0,m1) REP(j,i+1,m1) {
    			adde(i,j);
    		}
    	} else { //even
    		REP(i,0,m1) REP(j,i+1,m1) {
    			adde(i,j);
    		}
    		for(int i=1; i<m1-1; i+=2) {
    			adde(i,i+1);
    		}
    	}
    	ansn=0;
    	go(0);
    	bool fi=false;
    	REP(i,ansn,n) {
    		if(fi) putchar(' '); else fi=true;
    		putchar('1');
    	}
    	while(ansn--) {
    		printf(" %d", ans[ansn]);
    	}
    	putchar('
    ');
    }
    
    

    I. 堡堡的宝藏

    网格图可以看作一个二分图,然后题目给出了一系列(x+ygeq w(x,y))的条件。
    回忆(KM)算法,左部顶标(l(x)),右部顶标(r(x)),无论何时都满足(l(x_i)+r(y_i)geq w(x_i,y_i)),刚好符合题目中的限制条件。
    (KM)算法寻找的是相等子图的完备匹配,那么最后通过(KM)算法求得的答案,即为(sum l(x)+sum r(y))最小的情况,同时也是(sum w)最大的情况。
    所以该题直接施展(KM)算法即可。

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2020/2/8 19:53:15
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 1500 + 5;
    
    namespace R {
        int n;
        int w[N][N], kx[N], ky[N], py[N], vy[N], slk[N], pre[N];
        ll KM() {
            fill(kx, kx + n + 1, 0);
            fill(ky, ky + n + 1, 0);
            fill(py, py + n + 1, 0);
            for(int i = 1; i <= n; i++) 
                for(int j = 1; j <= n; j++)
                    kx[i] = max(kx[i], w[i][j]);
                    
            for(int i = 1; i <= n; i++) {
                fill(vy, vy + n + 1, 0);
                fill(slk, slk + n + 1, INF);
                fill(pre, pre + n + 1, 0);
                int k = 0, p = -1;
                for(py[k = 0] = i; py[k]; k = p) {
                    int d = INF;
                    vy[k] = 1;
                    int x = py[k];
                    for(int j = 1; j <= n; j++)
                        if (!vy[j]) {
                            int t = kx[x] + ky[j] - w[x][j];
                            if (t < slk[j]) { slk[j] = t; pre[j] = k; }
                            if (slk[j] < d) { d = slk[j]; p = j; }
                        }
                    for(int j = 0; j <= n; j++)
                        if (vy[j]) { kx[py[j]] -= d; ky[j] += d; }
                        else slk[j] -= d;
                }
                for (; k; k = pre[k]) py[k] = py[pre[k]];
            }
            ll ans = 0;
            for(int i = 1; i <= n; i++) ans += kx[i] + ky[i];
            return ans;
        }
    }
    
    int n, m, k;
    int id[N];
    int t1, t2;
    
    int get(int x, int y) {
        return (x - 1) * m + y;   
    }
    
    void run(){
        cin >> n >> m >> k;
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++) {
                id[get(i, j)] = ((i + j) & 1) ? ++t1 : ++t2;
            }   
        }
        for(int i = 1; i <= k; i++) {
            int x1, x2, y1, y2, w;
            cin >> x1 >> y1 >> x2 >> y2 >> w;
            if((x1 + y1) & 1) swap(x1, x2), swap(y1, y2);
            R::w[id[get(x1, y1)]][id[get(x2, y2)]] = max(R::w[id[get(x1, y1)]][id[get(x2, y2)]], w);
        }
        R::n = max(t1, t2);
        ll ans = R::KM();
        cout << ans << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
        return 0;
    }
    

    J. 邦邦的2-SAT模板

    给出一份2-SAT的模板:

    模板
    #include<cstdio>
    using namespace std;
    const int N=3010;
    int g[N<<1],nxt[N<<1],v[N<<1],num;
    int q[N<<1],t;
    bool vis[N<<1];
    int CNT;
    int n,m;
    void add(int x,int y){
    	nxt[++num]=g[x];
    	v[num]=y;
    	g[x]=num;
    }
    bool dfs(int x){
    	CNT++;
    	if(vis[x>n?x-n:x+n])return 0;
    	if(vis[x])return 1;
    	vis[q[++t]=x]=1;
    	for(int i=g[x];i;i=nxt[i])if(!dfs(v[i]))return 0;
    	return 1;
    }
    bool solve(){
    	for(int i=1;i<=n;i++)if(!vis[i]&&!vis[i+n]){
    		t=0;
    		if(!dfs(i)){
    			while(t)vis[q[t--]]=0;
    			if(!dfs(i+n))return 0;
    		}
    	}
    	return 1;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++)
    	{
    		int x,y;scanf("%d%d",&x,&y);
    		if(x<0)x=n-x;if(y<0)y=n-y;
    		add(x>n?x-n:x+n,y);add(y>n?y-n:y+n,x);
    	}
    	solve();
    	return 0;
    }
    
    现在要构造一组数据卡掉这个模板。

    构造如下:
    (1leq i<n,-i ightarrow i+1)
    (-n ightarrow -n)
    因为模板中要从(i)找到(-i)才罢休,这样构造出一条链的形式,最终复杂度就为(O(n^2))的。
    画个图就显然了。

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2020/2/9 10:43:20
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 1e5 + 5;
    
    void run(){
        int n; cin >> n; 
        cout << n << '
    ';
        for(int i = 1; i < n; i++) cout << -i << ' ' << i + 1 << '
    ';
        cout << -n << ' ' << -n << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
        return 0;
    }
    

    K. 破忒头的匿名信

    显然,最暴力的想法就是直接在(AC)自动机上(dp),但是每次暴力跳(fail)指针复杂度可能会达(O(n^2))
    但这个题有个性质不容易被注意到,就是所有字符串长度的总和不超过(5cdot 10^5),也就是说按照长度分类,字符串只有(O(sqrt{n}))级别。
    记得处理(fail)指针的时候优化一下,就是直接跳到为单词末尾的结点,直接略过那些没用的结点。这样复杂度就是比较严格的(O(nsqrt{n}))了。
    代码如下:

    Code
    #include<bits/stdc++.h>
    typedef long long ll;
    typedef unsigned long long ull;
    typedef double db;
    const int MAXN = 5e5+5,MAXM = 1e6+5,MOD = 998244353,INF = 0x3f3f3f3f,N=2e5;
    const ll INFL = 0x3f3f3f3f3f3f3f3f;
    const db eps = 1e-7;
    #define lson o<<1,l,m
    #define rson o<<1|1,m+1,r
    #define mid l + ((r-l)>>1)
    #define pii pair<int,int>
    #define vii vector<pii>
    #define vi vector<int>
    #define x first
    #define y second
    using namespace std;
    
    int n,p[MAXN],len[MAXN],ed[MAXN],last[MAXN],ch[MAXN][26],g[MAXN],tot;
    char s[MAXN],s2[MAXN];
    ll f[MAXN];
    void insert(char *s,int k){
        int now=0;
        for(int i=1;i<=len[k];i++){
            if(!ch[now][s[i]-'a']){
                ch[now][s[i]-'a'] = ++tot;
                memset(ch[tot],0,sizeof(ch[tot]));
                ed[tot] = last[tot] = g[tot] = 0;
            }
            now = ch[now][s[i]-'a'];
        }
        if(ed[now]){
            if(p[ed[now]] > p[k])ed[now] = k;
        }else ed[now] = k;
    }
    
    void build(){
        queue<int> q;
        for(int i=0;i<26;i++){
            if(ch[0][i]){
                last[ch[0][i]] = g[ch[0][i]]=0;
                q.push(ch[0][i]);
            }
        }
        while(q.size()){
            int x = q.front();q.pop();
            for(int i=0;i<26;i++){
                if(ch[x][i]){
                    last[ch[x][i]] = ch[last[x]][i];
                    if(ed[last[ch[x][i]]])g[ch[x][i]] = last[ch[x][i]];
                    else g[ch[x][i]] = g[last[ch[x][i]]];
                    q.push(ch[x][i]);
                }else ch[x][i] = ch[last[x]][i];
            }
        }
    }
    
    void init(){
        tot=0;
        memset(ch[0],0,sizeof(ch[0]));
        ed[0] = last[0] = 0;
    }
    
    int main(){
        ios::sync_with_stdio(false);cin.tie(0);
        while(cin>>n){
            init();
            for(int i=1;i<=n;i++){
                cin>>(s2+1) >> p[i];
                len[i] = strlen(s2+1);
                insert(s2,i);
            }
            cin>>(s+1);
            build();
            int x=0;
            int length = strlen(s+1);
            for(int i=1;i<=length;i++){
                f[i] = INFL;
                x = ch[x][s[i]-'a'];
                for(int y=x;y;y=g[y]){
                    if(ed[y] && f[i-len[ed[y]]] != INFL){
                        //cout<<i<<' '<<y<<'
    ';
                        f[i] = min(f[i], f[i-len[ed[y]]] + p[ed[y]]);
                    }
                }
            }
            if(f[length] == INFL)cout<<"-1
    ";
            else cout<<f[length]<<'
    ';
        }
        return 0;
    }
    
  • 相关阅读:
    课后作业之评价
    课堂作业之寻找水王
    构建之法阅读笔记04
    课下作业
    构建之法阅读笔记03
    学习进度条九
    学习进度条八
    冲刺第五天
    构建之法阅读笔记02
    冲刺第四天
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/12269634.html
Copyright © 2020-2023  润新知