2020 CCPC-Wannafly Winter Camp Day2


PTA
牛客

A. 托米的字符串

顯然答案為:

\[\frac{n(n+1)}{2}\cdot\sum_{len=1}^n\frac{f(len)}{len} \]

其中\(f(i)\)表示長度為\(i\)的所有串中含元音的個數。
顯然\(f(1)\)易求,那么\(\displaystyle f(2)=f(1)+\sum_{i=2}^{n-1}[s_i\in\{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 << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
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 << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
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) << '\n';
}

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<<'\n';
    }
    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 << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
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 << " \n"[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 << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
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] << '\n';
}

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 << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
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 << '\n';
    }
}

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\n1 2"); return 0;}
	if(n==3) {puts("2\n1 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\n", 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('\n');
}

I. 堡堡的寶藏

網格圖可以看作一個二分圖,然后題目給出了一系列\(x+y\geq 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 << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
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 << '\n';
}

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;
}
現在要構造一組數據卡掉這個模板。

構造如下:
\(1\leq i<n,-i\rightarrow i+1\)
\(-n\rightarrow -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 << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
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 << '\n';
    for(int i = 1; i < n; i++) cout << -i << ' ' << i + 1 << '\n';
    cout << -n << ' ' << -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)\)
但這個題有個性質不容易被注意到,就是所有字符串長度的總和不超過\(5\cdot 10^5\),也就是說按照長度分類,字符串只有\(O(\sqrt{n})\)級別。
記得處理\(fail\)指針的時候優化一下,就是直接跳到為單詞末尾的結點,直接略過那些沒用的結點。這樣復雜度就是比較嚴格的\(O(n\sqrt{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<<'\n';
                    f[i] = min(f[i], f[i-len[ed[y]]] + p[ed[y]]);
                }
            }
        }
        if(f[length] == INFL)cout<<"-1\n";
        else cout<<f[length]<<'\n';
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM