2022ICPC网络赛预选赛第二场题解,K题L题

K题意

在二维坐标系上给出一些圆和正方形,把他们涂黑,求所有图形的周长和。

思路

  • 有负数坐标,先加一个数字映射到正数域
  • 直线直接枚举,看方格的位置关系,注意细节
  • 曲线枚举格子的情况,看在这个格子里有多少 1 4 \frac{1}{4} 41圆就知道这个格子里曲线的长度
#include <bits/stdc++.h>

using namespace std;
using ll = long long;
using pii = pair<int, int>;

const int dirt = 1e2 + 5;
const int Max = 3e2 + 5;
const int mod = 998244353;
const int inv6 = 166374059;

int sq[Max][Max], cir[Max][Max], e[Max][Max][8];
int ans1 = 0, ans2 = 0, n;
const int dx[] = {0, 1, 1, 1, 0, -1, -1, -1}; 
const int dy[] = {1, 1, 0, -1, -1, -1, 0, 1};
const int ddx[] = {1, 2, 2, 1, -1, -2, -2, -1};
const int ddy[] = {2, 1, -1, -2, -2, -1, 1, 2};

int have(int x, int y) {
	return sq[x][y] || cir[x][y];
}
int cover(int x, int y) {
	if(sq[x][y] || sq[x + 1][y] || sq[x + 1][y + 1] || sq[x][y + 1]) return 1;
	if(cir[x][y] && cir[x + 1][y + 1]) return 1;
	if(cir[x + 1][y] && cir[x][y + 1]) return 1;
	return 0;
}
int main() {
	set<pair<pii, pii>> st;
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) {
		int op, x, y;
		scanf("%d %d %d", &op, &x, &y);
		x += dirt, y += dirt;
		if(op == 1) {
			sq[x][y] = 1;
			for(int j = 0; j < 8; j++) e[x][y][j] = 1;
		}
		else {
			cir[x][y] = 1;
		}
	}
	for(int i = 2; i < Max - 2; i++) {
		for(int j = 2; j < Max - 2; j++) {
			if(sq[i][j] && sq[i][j + 2]) e[i][j][0] = e[i][j][7] = 0;
			if(sq[i][j] && sq[i + 2][j]) e[i][j][1] = e[i][j][2] = 0;
			if(sq[i][j] && sq[i][j - 2]) e[i][j][3] = e[i][j][4] = 0;
			if(sq[i][j] && sq[i - 2][j]) e[i][j][5] = e[i][j][6] = 0;
			for(int k = 0; k < 8; k++) {
				if(e[i][j][k] && (have(i + dx[k], j + dy[k]) || have(i + dx[(k + 1) % 8], j + dy[(k + 1) % 8]) || sq[i + ddx[k]][j + ddy[k]])) 
					e[i][j][k] = 0;
				if(e[i][j][k]) {
					auto u = pair<int, int>(i + dx[k], j + dy[k]), v = pair<int, int>(i + dx[(k + 1) % 8], j + dy[(k + 1) % 8]);
					if(u > v) swap(u, v);
					st.insert({u, v});
				}
			}
		}
	}
	for(int i = 0; i < Max - 1; i++) {
		for(int j = 0; j < Max - 1; j++) {
			if(cover(i, j)) continue;
			if(cir[i][j] + cir[i + 1][j] + cir[i + 1][j + 1] + cir[i][j + 1] == 1) ans2 += 3;
			if(cir[i][j] + cir[i + 1][j] + cir[i + 1][j + 1] + cir[i][j + 1] == 2) ans2 += 2;
		}
	}
	printf("%d %lld\n", (int)st.size(), 1ll * ans2 * inv6 % mod);

	return 0;
}

L题意

给出一个字符串,再给出区间 [ l i , r i ] [l_i,r_i] [li,ri]的生成算法,求 [ l i , r i ] [l_i,r_i] [li,ri]中有多少个ICPC子序列

思路

容斥, X [ i ] X[i] X[i]的定义为到 i i i为止 X X X序列出现的次数,因为ICPC=I+CPC或者IC+PC或者ICP+C,所以我们从大到小计算每个序列的合法数量进行容斥即可

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

const int Maxn = 2e6 + 7;

constexpr int Mod = 998244353;
// assume -P <= x < 2P
int norm(int x) {if(x < 0)x += Mod;if (x >= Mod)x -= Mod;return x;}
template<class T>T power(T a, int b){T res = 1;for (; b; b /= 2, a *= a)if (b & 1)res *= a;return res;}
struct Mint {
    int x;Mint(int x = 0) : x(norm(x)){}
    int val() const {return x;}
    Mint operator-() const {return Mint(norm(Mod - x));}
    Mint inv() const {assert(x != 0);return power(*this, Mod - 2);}
    Mint &operator*=(const Mint &rhs) { x = ll(x) * rhs.x % Mod; return *this;}
    Mint &operator+=(const Mint &rhs) { x = norm(x + rhs.x); return *this;}
    Mint &operator-=(const Mint &rhs) { x = norm(x - rhs.x); return *this;}
    Mint &operator/=(const Mint &rhs) {return *this *= rhs.inv();}
    friend Mint operator*(const Mint &lhs, const Mint &rhs) {Mint res = lhs; res *= rhs; return res;}
    friend Mint operator+(const Mint &lhs, const Mint &rhs) {Mint res = lhs; res += rhs; return res;}
    friend Mint operator-(const Mint &lhs, const Mint &rhs) {Mint res = lhs; res -= rhs; return res;}
    friend Mint operator/(const Mint &lhs, const Mint &rhs) {Mint res = lhs; res /= rhs; return res;}
};

char s[Maxn];
Mint I[Maxn], C[Maxn], P[Maxn], IC[Maxn], CP[Maxn], PC[Maxn], ICP[Maxn], CPC[Maxn], ICPC[Maxn];

Mint get_PC(int l, int r) {
    return PC[r] - PC[l - 1] - P[l - 1] * (C[r] - C[l - 1]);
}

Mint get_CPC(int l, int r) {
    return CPC[r] - CPC[l - 1]
           - CP[l - 1] * (C[r] - C[l - 1])
           - C[l - 1] * get_PC(l, r);
}

Mint get_ICPC(int l, int r) {
    return ICPC[r] - ICPC[l - 1]
            - ICP[l - 1] * (C[r] - C[l - 1])
            - IC[l - 1] *  get_PC(l, r)
            - I[l - 1] * get_CPC(l, r);
}

int32_t main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    int n, q;
    cin >> n >> q >> s + 1;
    ll x, a, b, p;
    cin >> x >> a >> b >> p;
    for(int i = 1; i <= n; i++) {
        I[i] = I[i - 1], C[i] = C[i - 1], P[i] = P[i - 1];
        IC[i] = IC[i - 1], CP[i] = CP[i - 1], PC[i] = PC[i - 1];
        ICP[i] = ICP[i - 1], CPC[i] = CPC[i - 1], ICPC[i] = ICPC[i - 1];
        if(s[i] == 'I') {
            I[i] += 1;
        }
        if(s[i] == 'C') {
            C[i] += 1;
            IC[i] += I[i - 1];
            PC[i] += P[i - 1];
            CPC[i] += CP[i - 1];
            ICPC[i] += ICP[i - 1];
        }
        if(s[i] == 'P') {
            P[i] += 1;
            CP[i] += C[i - 1];
            ICP[i] += IC[i - 1];
        }
    }
    Mint ans = 0;
    vector<int> l(q), r(q);
    for(int i = 0; i < q; i++) {
        x = (a * x + b) % p;
        l[i] = x % n;
        l[i] += 1;
    }
    for(int i = 0; i < q; i++) {
        x = (a * x + b) % p;
        r[i] = x % n;
        r[i] += 1;
        if(l[i] > r[i]) swap(l[i], r[i]);
        ans += get_ICPC(l[i], r[i]);
    }
    cout << ans.val() << '\n';

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值