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;
}
posted @ 2020-02-06 18:02  heyuhhh  阅读(452)  评论(0编辑  收藏  举报