2019徐州网络赛 12/13

直到南昌的题都补完了才发现徐州的题我还没补(甚至blog都没写(((((((日哦
比完出来拿出手机发现beginend他们10多分钟前就AK了(而我连题都没补x

A

生搬硬套傻逼题。
显然就EXCRT求一下n,然后手推一下,发现,诶2 3 5都可以诶,4 6 7都不可以诶,再看看样例,8也可以诶。
这tmd不就是斐波那契数列吗
然后大力斐波那契就完事了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cstdint>
#include <utility>

using i128 = __int128;
using i64 = long long;

void Exgcd(i128 a, i128 b, i128& d, i128& x, i128& y) {
    if (b == 0) {
        d = a;
        x = 1, y = 0;
        return;
    }
    Exgcd(b, a % b, d, y, x);
    y -= x * (a / b);
}

std::pair<i64, i64> CRT(std::pair<i64, i64> a[], int n) {
    for (int i = 0; i < n; ++i) 
        if (a[i].first == -1)
            return { -1, -1 };
    i128 r1 = a[0].first, m1 = a[0].second;
    bool flag = 0;
    for (int i = 1; i < n; ++i) {
        i128 m2 = a[i].second, r2 = a[i].first, d, x, y;
        if (flag)
            continue;
        Exgcd(m1, m2, d, x, y);
        i128 c = r2 - r1;
        if (c % d) {
            flag = 1;
            continue;
        }
        i128 t = m2 / d;
        x = (c / d * x % t + t) % t;
        r1 = m1 * x + r1;
        m1 = m1 * m2 / d;
        r1 %= m1;
    }
    if (flag)
        return { -1, -1 };
    return { r1, m1 };
}

std::pair<i64, i64> a[15];

int main() {
    std::ios::sync_with_stdio(0);
    i64 n;
    std::cin >> n;
    for (int i = 0; i < n; ++i)
        std::cin >> a[i].second >> a[i].first;
    auto ans = CRT(a, n);
    if (ans.first == -1) {
        std::cout << "Tankernb!" << '\n';
        return 0;
    }
    n = ans.first;
    i64 t1 = 1, t2 = 2;
    for (; t2 <= n; t1 += t2, std::swap(t1, t2)) {
        if (t2 == n) {
            std::cout << "Lbnb!" << '\n';
            return 0;
        }
    }
    std::cout << "Zgxnb!" << '\n';
}

B

傻叉卡常题,显然就把每个数和这个数+1放一起处理一下就好了。
可是计蒜客竟然 200 w 200w 200w次set操作就飞了????(赛后看群发现加超级快读就行。
然后当时就改成了离散并查集了。

#include <iostream>
#include <algorithm>

using namespace std;

inline int read() {
    int d = 0;
    char s = getchar();
    while (s < '0' || s > '9') {
        s = getchar();
    }
    while (s >= '0' && s <= '9') {
        d = d * 10 + s - '0';
        s = getchar();
    }
    return d;
}

int const N = 1000005;

struct node {
    int x, y;
    int pos;
}q[N];

int f[N << 1];
int det[N << 1];
int cnt;
int n, m;

int unfind(int x) {
    int root = x, t;
    while (f[root] != root) 
        root = f[root];
    while (x != f[x]) {
        t = f[x];
        f[x] = root;
        x = f[x];
    }
    return root;
}

int main() {
    int n = read(), m = read();
    for (int i = 1; i <= m; ++i) {
        q[i].x = read(), q[i].y = read();
        det[++cnt] = q[i].y;
        if (q[i].y < n)
            det[++cnt] = q[i].y + 1;
    }
    det[++cnt] = n + 1;
    sort(det + 1, det + cnt + 1);
    cnt = unique(det + 1, det + cnt + 1) - (det + 1);
    for (int i = 1; i <= m; ++i) 
        q[i].pos = lower_bound(det + 1, det + cnt + 1, q[i].y) - det;
    for (int i = 1; i <= cnt; ++i)
        f[i] = i;
    for (int i = 1; i <= m; ++i) {
        int x = q[i].x, y = q[i].y;
        if (x == 1) {
            f[q[i].pos] = q[i].pos + 1;
        } else {
            int dx = unfind(q[i].pos);
            if (dx == cnt)
                puts("-1");
            else
                printf("%d\n", det[dx]);
        }
    }
}

C

这是真的想给出题人塞一百本英语读本。
a half指不均等的一半海星?????不过赛后查了下a halves好像是可以表示不均等的两半,但是不管如何a half都是均等的两半(虽然还有其他的语法锅但是就不吐槽了。所以真正题目的意思就是能随便切成两块,然后每块都是2的倍数就行。

#include <iostream>
#include <cstdio>

using namespace std;

int main() {
    int n;
    cin >> n;
    if (n != 2 && (n & 1) == 0)
        puts("YES");
    else
        puts("NO");
}

D

kmp直接判就完事了

#include <iostream>
#include <string.h>
using namespace std;

const int maxn = 100010;

int nxts[maxn], nxtt[maxn];

void getnxt(char* s,int* nxt, int n) {
    int i = 0,j = -1;
    nxt[0] = -1;
    while(i < n) {
        while(j != -1 && s[i] != s[j]) j = nxt[j];
        ++i,++j;
        nxt[i] = j;
    }
}

bool kmp(char* s,char* t, int* nxt, int n, int m) {
    int i = 0,j = 0;
    while(i < n) {
        while(j != -1 && s[i] != t[j]) j = nxt[j];
        ++i;++j;
        if(j == m) return true;
    }
    return false;
}

char s[maxn];
char t[maxn];

int main() {
    scanf("%s",s);
    int n = strlen(s);
    getnxt(s, nxts, n);
    int T;
    scanf("%d",&T);
    while(T--) {
        scanf("%s",t);
        int m = strlen(t);
        if(m == n) {
            if(kmp(t, s, nxts, m, n)) puts("jntm!");
            else puts("friend!");
        }
        else if(m > n) {
            if(kmp(t, s, nxts, m, n)) puts("my teacher!");
            else puts("senior!");
        }
        else {
            getnxt(t, nxtt, m);
            if(kmp(s, t, nxtt, n, m)) puts("my child!");
            else puts("oh, child!");
        }
    }
    return 0;
}

E

倒过来维护一个序列,然后二分就完事了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>

int main () {
    std::ios::sync_with_stdio(false);
    
    int n, m;
    std::cin >> n >> m;
    
    auto w = std::vector<int>(n);
    for (auto &c: w)
        std::cin >> c;
    
    auto ss = std::vector<std::pair<int, int>>(n);
    ss.reserve(n);
    auto ans = std::vector<int>(n, 0);
    for (int i = n - 1; i >= 0; --i) {
        auto it = std::lower_bound(std::begin(ss), std::end(ss), std::make_pair(w[i] + m, 0));
    	ans[i] = (it == std::end(ss) ? -1 : it->second - i - 1);
        
        if (ss.empty() || ss.back().first < w[i])
        	ss.push_back({ w[i], i });
    }
    
    bool fst = true;
    for (auto c: ans) {
        if (fst)
            fst = false;
    	else
            std::cout << ' ';
        std::cout << c;
    }
    std::cout << '\n';
    
	return 0;
}

F

G

直接PAM上计数就好

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;

int const N = 300015;

int nxt[N][26];
int fail[N];
int len[N];
int s[N], R;
int cont[N];
int cnt[N];
int col[N];
int last;
int p;

int newNode(int _len) {
    for (int i = 0; i < 26; ++i)
        nxt[p][i] = 0;
    cont[p] = 0;
    len[p] = _len;
    return p++;
}

void init(int n) {
    p = 0;
    newNode(0);
    newNode(-1);
    fill(begin(s), end(s), -1);
    fill(begin(cnt), end(cnt), 0);
    last = 1;
    fail[0] = 1;
}

int getFail(int v) {
    int bord = R;
    int cons = -1;
    while (s[bord + cons * (len[v] + 1)] != s[bord])
        v = fail[v];
    return v;
}

void add(int c) {
    s[++R] = c;
    int cur = getFail(last);
    if (nxt[cur][c] == 0) {
        int now = newNode(len[cur] + 2);
        fail[now] = nxt[getFail(fail[cur])][c];
        nxt[cur][c] = now;
        cont[now] = cont[fail[now]] + 1;
        col[now] = col[cur] | (1 << c);
    }
    last = nxt[cur][c];
    ++cnt[last];
}

void count() {
    for (int i = p - 1; i > 1; --i)
        cnt[fail[i]] += cnt[i];
}

int main() {
    string s;
    cin >> s;
    init(s.size());
    for (auto c : s)
        add(c - 'a');
    count();
    long long ans = 0;
    for (int i = 0; i < p; ++i)
        ans += 1ll * __builtin_popcount(col[i]) * cnt[i];
    cout << ans << '\n';
}

H

第一次写min25筛,学了一波,发现就是一个奇妙的筛法然后合并和一些贡献然后每次可以一起算。
在这道题的话就是
∑ i = 1 n f ( i ! ) = ∑ p   i s   p r i m e ∑ p k ≤ n ( n + 1 ) [ n p k ] − p k C ( [ n p ] + 1 , 2 ) \sum_{i = 1}^n f(i!)=\sum_{p \ is \ prime} \sum_{p^k \leq n} (n +1) [\frac{n}{p^k}] - p^k C([\frac{n}{p}] +1, 2) i=1nf(i!)=p is primepkn(n+1)[pkn]pkC([pn]+1,2)
那么对于 k ≥ 2 k \geq 2 k2时,直接暴力算贡献,对于 k = 1 k=1 k=1时,可以min25处理素数个数和素数和即可。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;

using ll = long long;

int const M = 200005;
int const mod = 998244353;
int const inv2 = (mod + 1) / 2;
int const LIM = 100000;

ll id1[M], id2[M], w[M << 2], f[M << 1], g[M << 1], cnt, s[M];
ll n;
bool isp[M];
int pri[M], tot;

int get_id(ll x) {
	if (x < LIM)
		return id1[x];
	return id2[n / x];
}

void SAI() {
	isp[1] = 1;
	for (int i = 2; i < LIM; ++i) {
		if (isp[i] == 0) {
			pri[++tot] = i;
			s[tot] = (s[tot - 1] + i) % mod;
		}
		for (int j = 1; j <= tot && pri[j] * i < LIM; ++j) {
			isp[pri[j] * i] = 1;
			if (i % pri[j] == 0) 
				break;
		}
	}
}

ll C2(ll x) {
	x %= mod;
	return x * (x + 1) % mod * inv2 % mod;
}

int main() {
	cin >> n;
	SAI();
	for (ll l = 1, r; l <= n; l = r + 1) {
		ll t = n / l;
		r = n / t;
		w[++cnt] = t;
		f[cnt] = t - 1;
		g[cnt] = C2(t) - 1;
		if (t < LIM)
			id1[t] = cnt;
		else
			id2[r] = cnt;
	}
	for (int j = 1; j <= tot; ++j) 
		for (int i = 1; i <= cnt && 1ll * pri[j] * pri[j] <= w[i]; ++i) {
			ll t = w[i] / pri[j];
			int k = get_id(t);
			f[i] = f[i] - f[k] - 1 + j;
			g[i] = (g[i] + mod - 1ll * pri[j] * (g[k] - s[j - 1]) % mod) % mod;
		}
	ll ans = 0;
	for (int i = 1; i <= tot; ++i) 
		for (ll j = 1ll * pri[i] * pri[i]; j <= n; j = j * pri[i]) 
			ans = (ans + ((n + 1) % mod) * ((n / j) % mod) % mod - (j % mod) * C2(n / j) % mod + mod) % mod;
	for (ll l = 2, r; l <= n; l = r + 1) {
		r = n / (n / l);
		ll t = n / l;
		t %= mod;
		ans = (ans + ((n + 1) % mod) * t % mod * ((f[get_id(r)] - f[get_id(l - 1)] + mod) % mod) % mod) % mod;
		ans = (ans + 1ll * mod - 1ll * C2(t) * ((g[get_id(r)] - g[get_id(l - 1)] + mod) % mod) % mod) % mod;
	}
	cout << (ans + mod) % mod << '\n';
}

I

考虑把答案拆成 [ 1 , r ] [1,r] [1,r] [ 1 , l ] [1,l] [1,l]的答案差,然后再减去 [ 1 , l ] [1,l] [1,l]对于 [ 1 , r ] [1,r] [1,r]的影响。
那就树状数组统计一下二维偏序就好了。

#include <iostream>
#include <algorithm>
#include <vector>

struct Que {
    int l, r;
    int idx, ans;
};

int main () {
    std::ios::sync_with_stdio(false);
    
    int n, m;
    std::cin >> n >> m;
    auto v = std::vector<int>(n);
    auto from = std::vector<int>(n + 1);
    for (int i = 0; i < n; ++i) {
        std::cin >> v[i];
        from[v[i]] = i;
	}
	
    //auto sum1 = std::vector<int>(n);
    auto sum2 = std::vector<int>(n + 1);
    auto presum = [&](int x) {
    	int ret = 0;
    	for (++x; x; x -= x & -x)
    		ret += sum2[x];
    	return ret;
    };
	auto add = [&](int x, int dt) {
		for (++x; x <= n; x += x & -x)
			sum2[x] += dt;
	};
	
    auto tos = std::vector<std::vector<int>>(n);
    for (int i = 1; i <= n; ++i)
    	for (int j = i * 2; j <= n; j += i) {
    		int a = from[i];
			int b = from[j];
			if (a > b)
				std::swap(a, b);
			tos[a].push_back(b);
			//++sum1[b];
			add(b, 1);
		}
	//for (int i = 1; i < n; ++i)
	//	sum1[i] += sum1[i - 1];
    
    auto q = std::vector<Que>(m);
    for (int i = 0; i < m; ++i) {
        std::cin >> q[i].l >> q[i].r;
        --q[i].l;
        --q[i].r;
        q[i].idx = i;
    }
    
    std::sort(std::begin(q), std::end(q), [](const Que &a, const Que &b) {
    	return a.l < b.l;
	});
	
	int idx = 0;
	for (int l = 0; l < n; ++l) {
		//std::cerr << "l=" << l << '\n';
		
		for (; idx < m && q[idx].l == l; ++idx) {
			q[idx].ans = presum(q[idx].r);
			//std::cerr << "q[" << idx << "]=" << q[idx].ans << '\n'; 
		}
		
		for (int r: tos[l]) {
			add(r, -1);
			//std::cerr << "l=" << l << " add r=" << r << '\n';
		}
	}
	
    std::sort(std::begin(q), std::end(q), [](const Que &a, const Que &b) {
    	return a.idx < b.idx;
	});
	
	for (auto &c: q)
		std::cout << c.ans << '\n';
    
    return 0;
}

J

比赛的时候傻逼了。以为可以很方便的计算反面,结果漏了一些贡献。
我们直接设 f i f_i fi i i i这个子树到最深处的概率,就可以很方便的转移了。
哎白送了一题。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <cstring>

using namespace std;

using ll = long long;

int const N = 1000005;
ll const mod = 1e9 + 7;

ll KSM(ll a, ll k) {
    if (a < 0)
        a = (a % mod + mod) % mod;
    ll ret = 1;
    if (k == -1)
        k = mod - 2;
    for (; k; k >>= 1, a = 1ll * a * a % mod)
        if (k & 1)
            ret = 1ll * ret * a % mod;
    return ret;
}

int dep[N];
int mx[N];
int n;
ll du[N];
bool is[N];
int fa[N];
ll ans;
int f[N];

struct edge {
    int y, next;
} e[N << 1];
int last[N], ne;

void addedge(int x, int y) {
    e[++ne] = { y, last[x] };
    last[x] = ne;
}

void pre_dfs(int x, int pre) {
    fa[x] = pre;
    dep[x] = dep[fa[x]] + 1;
    mx[x] = dep[x];
    for (int i = last[x]; i != 0; i = e[i].next) {
        if (e[i].y == pre)
            continue;
        pre_dfs(e[i].y, x);
        du[x]++;
        mx[x] = max(mx[x], mx[e[i].y]);
    }
}

void dfs(int x) {
    if (du[x] == 0) {
		if (dep[x] == mx[1])
			f[x] = 1;
		else
			f[x] = 0;
        return;
	}
	ll sum = 0;
    for (int i = last[x]; i != 0; i = e[i].next) {
        if (e[i].y == fa[x])
            continue;
		dfs(e[i].y);
		sum += f[e[i].y];
    }
	sum = sum * KSM(du[x], -1);
	sum = 1 - sum;
	f[x] = mod + 1 - KSM(sum, du[x]);
	f[x] %= mod;
}

int main() {
    ios::sync_with_stdio(0);
    cin >> n;
    for (int i = 1; i < n; ++i) {
        int x, y;
        cin >> x >> y;
        addedge(x, y);
        addedge(y, x);
    }
    pre_dfs(1, 0);
    dfs(1);
    cout << f[1] << '\n';
}

K

#include <iostream>
#include <vector>
#include <map>
#include <algorithm>

int main () {
    std::ios::sync_with_stdio(false);
    
    int n;
    std::cin >> n;
    auto pts = std::vector<std::pair<int, int>>(n);
    for (auto &p: pts)
        std::cin >> p.first >> p.second;
    
    auto mp = std::map<std::pair<int, int>, int>();
    for (int i = 0; i < n; ++i)
        for (int j = i + 1; j < n; ++j) {
            auto mid = std::make_pair(pts[i].first + pts[j].first, pts[i].second + pts[j].second);
            mp[mid] -= 2;
        }
    for (auto &p: pts)
        mp[std::make_pair(p.first * 2, p.second * 2)] -= 1;
    
    int mn = 0;
    for (auto &it: mp)
        mn = std::min(mn, it.second);
    std::cout << n + mn << '\n';
    
    return 0;
}

L

调参党的胜利x
最后时刻随便改了下参数过了。。。。。
显然你考虑一下每个地方去到另一个地方的最少翻转次数,那么就可以直接DP哈密顿回路了。

#include <iostream>
#include <vector>
#include <algorithm>

using P = std::pair<int, int>;

int len (P const &a, P const &b) {
    int dx = std::abs(a.first - b.first);
    int dy = std::abs(a.second - b.second);
    if (dx > dy)
        std::swap(dx, dy);
    
    if (dx >= 2)
        return dx + dy;
    else if (dx == 1) {
        if (dy >= 2) {
            if (dy % 4 == 0)
                return dx + dy;
            else if (dy % 4 == 2)
                return dx + dy + 2;
            else
                return dx + dy + 2;
        } else { // dx == 1
            return 6;
        }
    } else { // dx == 0
        if (dy >= 2) {
            if (dy % 4 == 0)
                return dx + dy;
            else
                return dx + dy + 2;
        } else if (dy == 1) {
            return 3;
        } else { // dy == 0
            return 0;
        }
    }
}

const int N = 17;
int f[1 << N][N];

int main () {
    std::ios::sync_with_stdio(false);
    
    int t;
    std::cin >> t;
    while (t--) {
        int n;
        std::cin >> n;
        auto v = std::vector<P>(n);
        for (auto &c: v)
        	std::cin >> c.first >> c.second;
        
        for (int rest = 1; rest < (1 << n); ++rest)
            for (int beg = 0; beg < n; ++beg)
                f[rest][beg] = 0x7fffffff;
        for (int beg = 0; beg < n; ++beg)
        	f[0][beg] = 0;
        
        for (int rest = 1; rest < (1 << n); ++rest)
            for (int beg = 0; beg < n; ++beg)
                for (int i = 0; i < n; ++i)
                    if (rest & (1 << i)) {
                        f[rest][beg] = std::min(
                            f[rest][beg],
                            f[rest ^ (1 << i)][i] + len(v[beg], v[i])
                        );
                    }
        
        int ans = 0x7fffffff;
        for (int i = 0; i < n; ++i)
            ans = std::min(ans, f[(1 << n) - 1][i] + len({ 0, 0 }, v[i]));
        
        std::cout << ans << '\n';
    }
    
    return 0;
}

M

被队友灌输了假题意,子序列变成子串,写了好多版本的代码都WA(题目都错了还想AC?
什么SAM,后缀树,exkmp啥的都写了一遍x
最后都exkmp了,觉得不可能啊,这怎么还会WA,我去看了下题x,发现了真相。
一个序列自动机就完事了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>

using namespace std;

int const N = 1000005;

int nxt[N][26];
char s[N], t[N];
int n, m;

int main() {
    ios::sync_with_stdio(0);
    cin >> n >> m >> (s + 1) >> (t + 1);
    for (int i = 0; i < 26; ++i)
        nxt[n + 1][i] = n + 1;
    for (int i = n; i >= 1; --i) {
        for (int j = 0; j < 26; ++j)
            nxt[i][j] = nxt[i + 1][j];
        nxt[i][s[i] - 'a'] = i;
    }
    int p = 0, ans = -1;
    for (int i = 1; i <= m; ++i) {
        for (int j = t[i] - 'a' + 1; j < 26; ++j)
            if (nxt[p + 1][j] <= n) 
                ans = max(ans, n - nxt[p + 1][j] + i);
        p = nxt[p + 1][t[i] - 'a'];
        if (p == n + 1)
            break;
    }
    if (p < n)
        ans = max(ans, n - p + m);
    cout << ans << '\n';
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值