2022“杭电杯”中国大学生算法设计超级联赛(1) 题解

A. String

首先通过KMP建立失配树,节点u的所有祖先都为其border,满足第二个条件。

对于条件三,我们需要满足 ① 必须有相交部分,② 相交部分长度被 \(k\) 整除。

对于①,我们在失配树上DFS,并利用双指针维护当前链,保证当前链的长度\(len > \lfloor \frac{u}{2} \rfloor\),其中 \(u\) 是当前DFS到的节点。

对于②,满足条件的长度 \(v\)\((2v - u) \% k == 0\) ,即 \(2v \% k == u \% k\)。我们在双指针的过程中维护一个桶记录查询即可。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int MAXN = 1e6 + 5;
const int MOD = 998244353;

vector<int> G[MAXN], dfs;
char S[MAXN];
int n, k, pmt[MAXN], ans, cnt[MAXN], s[MAXN], L, R, dfssize, st[MAXN], cur[MAXN];

void Solve() {
	cin >> S + 1 >> k;
	n = strlen(S + 1); L = 1; R = 0;
	int p = 0; ans = 1;
	for(int i = 0; i <= n; i++) G[i].clear();
	for(int i = 0; i <= k; i++) cnt[i] = 0;
	G[0].push_back(1);
	for(int i = 2; i <= n; i++) {
		while(p && S[p + 1] != S[i]) p = pmt[p];
		if(S[p + 1] == S[i]) p++;
		pmt[i] = p;
		G[ pmt[i] ].push_back(i);
	}
	//for(int i = 1; i <= n; i++) cout << pmt[i] << " "; cout << "pmt\n";
	
	dfs.clear(); dfs.push_back(0); dfssize = 1;
	while(dfssize) {
		int u = dfs.back();
		if(!st[u]) {
			cur[u] = L;
			s[++R] = u, cnt[2 * u % k]++;
			while(L <= R && 2 * s[L] <= u) cnt[2 * s[L] % k]--, L++;
			ans = ans * (cnt[u % k] + 1) % MOD;		
			for(auto v : G[u]) dfs.push_back(v), dfssize++;
			st[u] ^= 1;
		} else {
			while(L > cur[u]) L--, cnt[2 * s[L] % k]++;
			R--, cnt[2 * u % k]--;				
			dfs.pop_back(); dfssize--;
			st[u] ^= 1;
		}
	}
	
	cout << ans << "\n";
}

signed main()
{	
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; cin >> T;
	while(T--) Solve();
	return 0; 
}

B. Dragon slayer

发现墙最多只有15个,状压每一个墙的状态(是否被消除),直接爆搜即可。

时间复杂度\(O(n m 2^k)\),略微卡常,需要注意写法。

#pragma GCC optimize(2)
//#define int long long
#include<bits/stdc++.h>
#define x1 x123456789
#define y1 y123456789
#define x2 x12345678
#define y2 y12345678
#define fi first
#define se second
#define pii pair<int, int>
#define pir make_pair
using namespace std;

const int MAXN = 30 + 5;

int dx[5] = {0, 1, 0, -1}, dy[5] = {1, 0, -1, 0};
int X1[MAXN], X2[MAXN], Y1[MAXN], Y2[MAXN];
int n, m, K, xs, ys, xt, yt, vis[MAXN][MAXN], dist[MAXN][MAXN], wall[MAXN][MAXN];
queue<pii> Q;

int Hash(int x) { return x << 1; }

int Solve(int st) {
	for(int i = 0; i <= 2 * n; i++) for(int j = 0; j <= 2 * m; j++) vis[i][j] = wall[i][j] = 0;
	for(int i = 1; i <= K; i++) {
		if((st >> i - 1) & 1) continue;
		int x1 = X1[i], x2 = X2[i], y1 = Y1[i], y2 = Y2[i];
		if(x1 == x2) {
			if(y1 > y2) swap(y1, y2);
			for(int j = y1; j <= y2; j++) wall[x1][j] = 1;
		} else {
			if(x1 > x2) swap(x1, x2);
			for(int j = x1; j <= x2; j++) wall[j][y1] = 1;
		}
	}
	while(!Q.empty()) Q.pop();
	Q.push( pir(xs, ys) );
	//cout << st << " st:\n";
	while(!Q.empty()) {
		pii cur = Q.front(); Q.pop();
		int x = cur.fi, y = cur.se;
		if(vis[x][y]) continue;
		//cout << x << " " << y << " " << w << " Q\n";
		vis[x][y] = 1;
		if(x == xt && y == yt) {
			int cnt = 0;
			for(int i = 0; i < K; i++) cnt += ((st >> i) & 1);
			//cout << st << " " << cnt << " res\n";
			return cnt;	
		}
		for(int k = 0; k < 4; k++) {
			int cx = x + dx[k], cy = y + dy[k];
			if(cx < 0 || cy < 0 || cx > 2 * n || cy > 2 * m || vis[cx][cy] || wall[cx][cy]) continue;
			if(cx % 2 == 0 && cy % 2 == 0) continue;
			cx += dx[k], cy += dy[k];
			if(cx < 0 || cy < 0 || cx > 2 * n || cy > 2 * m || vis[cx][cy] || wall[cx][cy]) continue;
			Q.push( pir(cx, cy) );
		}
	}
	return 0x3f3f3f3f;
}

void Solve() {
	cin >> n >> m >> K >> xs >> ys >> xt >> yt; 
	xs = Hash(xs) + 1, ys = Hash(ys) + 1, xt = Hash(xt) + 1, yt = Hash(yt) + 1;
	for(int i = 1; i <= K; i++) {
		int x1, x2, y1, y2; cin >> x1 >> y1 >> x2 >> y2;
		x1 = Hash(x1), y1 = Hash(y1), x2 = Hash(x2), y2 = Hash(y2);
		X1[i] = x1, X2[i] = x2, Y1[i] = y1, Y2[i] = y2;
	}
	int ans = 0x3f3f3f3f;
	for(int st = 0; st < (1 << K); st++) ans = min(ans, Solve(st) );
	cout << ans << "\n";
}

signed main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; cin >> T;
	while(T--) Solve();
	return 0; 
}

C. Backpack

\(f[i][j][k]\)为是否有合法的方案满足取前 \(i\) 个物品,异或和为 \(j\),当前容量为 \(k\)

\(f[i][j][k] = f[i - 1][j \oplus w][k - v] \ | \ f[i - 1][j][k]\),其中\(\oplus\)代表异或符号。

用滚动数组简化第一维,bitset维护第三维,有\(f[1][j]=(f[0][j \oplus w] << v) \ | \ f[0][j]\)

时间复杂度\(O(\frac{n^3}{32})。\)

#pragma GCC optimize(2)
//#define int long long
#include<bits/stdc++.h>
using namespace std;

const int MAXN = 1024 + 5;

int n, m;
bitset<1024> f[2][1024]; 

void Solve() {
	cin >> n >> m;
	memset(f, 0, sizeof f); f[0][0][0] = 1;
	for(int i = 1; i <= n; i++) {
		int v, w; cin >> v >> w;
		for(int k = 0; k < 1024; k++) f[1][k] = f[1 - 1][k] | (f[1 - 1][k ^ w] << v);
		for(int k = 0; k < 1024; k++) f[0][k] = f[1][k], f[1][k] = 0; 
	}
	int ans = -1;
	for(int k = 0; k < 1024; k++) if(f[0][k][m]) ans = max(ans, k);
	cout << ans << "\n";
}

signed main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T; cin >> T;
	while(T--) Solve();
	return 0; 
}

D. Ball

先筛出质数,由于数据上界为2e5,复杂度上界为\(\Theta(nlogn)\)的筛法都能过。

直接枚举三个点会超时,考虑只枚举两个点,其之间的距离作为median distance,设其为 \(mid\)

我们需要找出第三个点满足与之相连的两条边,其中一条边小于等于 \(mid\),另一条边大于等于 \(mid\)

我们把点对按长度从小到大排序,并且在遍历的过程中用 bitset 维护之前被遍历过的边。这样即可求出枚举两个点时,有多少个点符合要求

时间复杂度\(O(\frac{n^3}{32})\)

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int MAXN = 2000 + 5;

struct Node {
	int u, v, w;
	bool operator < (const Node &a) const { return w < a.w;	}
};

int n, m, X[MAXN], Y[MAXN], cnt, prime[200005], isprime[200005];
vector<Node> vec;
bitset<MAXN> Set[MAXN];

int Dist(int i, int j) {
	return abs(X[i] - X[j]) + abs(Y[i] - Y[j]);
}

void Solve() {
	scanf("%d%d", &n, &m); vec.clear();
	for(int i = 0; i < n; i++) {
		scanf("%d%d", &X[i], &Y[i]);	
		for(int j = 0; j < i; j++) vec.push_back( (Node){j, i, Dist(i, j)} );
		Set[i].reset();
	}
	sort(vec.begin(), vec.end());
	long long ans = 0;
	for(auto i : vec) {
		int u = i.u, v = i.v, w = i.w;
		if(!isprime[w]) ans += (Set[u] ^ Set[v]).count();
		Set[u][v] = Set[v][u] = 1;
	}
	printf("%lld\n", ans);
}

void Init() {
	n = 2e5;
	isprime[0] = isprime[1] = 1;
	for(int i = 2; i <= n; i++) {
		if(!isprime[i]) prime[++cnt] = i;
		for(int j = 1; j <= cnt && i * prime[j] <= n; j++) {
			isprime[ i * prime[j] ] = 1;
			if(i % prime[j] == 0) break;
		}
	}
}

signed main()
{
	Init();
	int T; scanf("%d", &T);
	while(T--) Solve();
	return 0; 
}

I. Laser

考虑其中一个点被4条直线覆盖的情况,此时有两种情况:

①所有点都被这条直线覆盖,直接返回YES。

②有一个点没被这条直线覆盖,再枚举这个点被另外3条直线覆盖的情况。此时我们可以通过这两条线求出交点,得到交点之后直接判断即可。共需要判断12种情况。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define pii pair<int, int>
#define x first
#define y second
#define pir make_pair
using namespace std;

const int MAXN = 5e5 + 5;

unordered_map<int, vector<int> > L[5];
int Map[5][MAXN], n, x[MAXN], y[MAXN];
int dx[5] = {0, 1, 1, 0, 1}, dy[5] = {0, -1, 1, 1, 0};

pii Find(int px, int py, int i, int j) {
    pii res; int k1, Dx, Dy;
    if(dy[j] == 0) {
        k1 = (y[py] - y[px]) / dy[i];
    } else {
        Dx = x[px] - x[py], Dy = y[px] - y[py];
        k1 = (Dx * dy[j] - Dy * dx[j]) / (dy[i] * dx[j] - dx[i] * dy[j]);    
    }
    res = pir(x[px] + dx[i] * k1, y[px] + dy[i] * k1);
    return res;
}

bool Check(pii p) {
    for(int i = 1; i <= n; i++) {
        bool flag = 0;
        for(int k = 1; k <= 4; k++) {
            int cur;
            if(k == 1) cur = p.x + p.y;
            if(k == 2) cur = p.x - p.y;
            if(k == 3) cur = p.x;
            if(k == 4) cur = p.y;
            if(Map[k][i] == cur) flag = 1;
        }
        if(!flag) return 0;
    }
    return 1;
}

void Solve() {
    cin >> n;
    for(int i = 1; i <= 4; i++) L[i].clear();
    for(int i = 1; i <= n; i++) {
        cin >> x[i] >> y[i];
        L[1][ x[i] + y[i] ].push_back(i);    // (x + k, y - k)
        L[2][ x[i] - y[i] ].push_back(i);    // (x + k, y + k)
        L[3][ x[i] ].push_back(i);            // (x, y + k)
        L[4][ y[i] ].push_back(i);            // (x + k, y)
        
        Map[1][i] = x[i] + y[i];
        Map[2][i] = x[i] - y[i];
        Map[3][i] = x[i];
        Map[4][i] = y[i];
    }
    for(int i = 1; i <= 4; i++) {
        for(int j = 1; j <= 4; j++) {
            if(i == j) continue;
            //for(int k = 1; k <= n; k++) vis[k] = 0;
            
            int px = 1, py = 2;
            for(; py <= n; py++) if(Map[i][px] != Map[i][py]) break;
            if(py > n) return void(cout << "YES\n");
            
            pii p = Find(px, py, i, j);
            if(Check(p)) return void(cout << "YES\n");
        }
    }
    return void(cout << "NO\n");
}

signed main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T; cin >> T;
    while(T--) Solve();
    return 0; 
}
posted @ 2022-07-26 09:43  Orzjh  阅读(132)  评论(0)    收藏  举报