ICPC2023 西安站

A. An Easy Geometry Problem

tag: 二分Hash

我们令 \(A_i' = A_i - i\times \frac k 2\)

那么我们可以发现:
\( \begin{aligned}A_{i+r}' - A_{i-r}' &= A_{i+r} - (i+r)\times \frac k 2 - (A_{i-r} - (i-r)\times \frac k 2) \\ & = A_{i+r} - A_{i-r} - kr \end{aligned}\)

所以我们将原本的 \(A_{i+r} - A_{i-r} = kr + b\) 变成了判断 \(A_{i+r}' = A_{i-r}' + b\)

这就可以使用 二分\(Hash\) 进行判断,带修就套一个线段树即可

code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll NN = 2e5 + 8,MOD = 1e9+7;
int n,q,k,b;
ll a[NN];
ll pw[NN],pwp[NN];
struct Seg
{
    int l,r;
    ll tag;
    ll lsum,rsum;
#define l(x) tree[x].l
#define r(x) tree[x].r
#define tag(x) tree[x].tag
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
#define lsum(x) tree[x].lsum
#define rsum(x) tree[x].rsum
#define len(x) tree[x].r-tree[x].l+1
}tree[NN << 2];
void addlz(int x,int num)
{
    tag(x) += num;
    lsum(x) += pwp[len(x)] * num;
    rsum(x) += pwp[len(x)] * num;
}
void pushdown(int x)
{
    addlz(ls(x),tag(x));
    addlz(rs(x),tag(x));
    tag(x) = 0;
}
void pushup(int x)
{
    lsum(x) = lsum(ls(x)) + lsum(rs(x)) * pw[len(ls(x))];
    rsum(x) = rsum(ls(x)) * pw[len(rs(x))] + rsum(rs(x));
}
void build(int x,int l,int r)
{
    l(x) = l; r(x) = r;
    if(l == r)
    {
        lsum(x) = a[l]+b;
        rsum(x) = a[l];
        return;
    }
    int mid = (l + r) / 2;
    build(ls(x),l,mid);
    build(rs(x),mid+1,r);
    pushup(x);
}
void modify(int x,int l,int r,int num)
{
    if(l <= l(x) && r(x) <= r)
    {
        addlz(x,num);
        return;
    }
    int mid = (l(x) + r(x)) / 2;
    pushdown(x);
    if(l <= mid) modify(ls(x),l,r,num);
    if(mid + 1 <= r) modify(rs(x),l,r,num);
    pushup(x);
}
ll queryl(int x,int l,int r)
{
    if(l <= l(x) && r(x) <= r) return lsum(x);
    int mid = (l(x) + r(x)) / 2;
    ll lans = 0,rans = 0,ans = 0;
    pushdown(x);
    if(l <= mid) lans = queryl(ls(x),l,r);
    if(mid + 1 <= r) rans = queryl(rs(x),l,r);
    
    ans = lans + rans * pw[max(0,mid - max(l,l(x)) + 1)];
    
    return ans;
}

ll queryr(int x,int l,int r)
{
    if(l <= l(x) && r(x) <= r) return rsum(x);
    int mid = (l(x) + r(x)) / 2;
    ll lans = 0,rans = 0,ans = 0;
    pushdown(x);
    if(l <= mid) lans = queryr(ls(x),l,r);
    if(mid + 1 <= r) rans = queryr(rs(x),l,r);
    ans = rans + lans * pw[max(min(r,r(x))-(mid+1)+1,0)];
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> q >> k >> b;
    pw[0] = 1;
    for(int i = 1; i <= n; ++i) pw[i] = pw[i-1] * MOD;
    for(int i = 1; i <= n; ++i) pwp[i] = pwp[i-1] + pw[i-1];
    
    for(int i = 1; i <= n; ++i) cin >> a[i];
    
    for(int i = 1; i <= n; ++i) a[i] = 2 * a[i] - k * i;
    b *= 2;
    
    build(1,1,n);
    while(q--)
    {
        int op;
        cin >> op;
        if(op == 1)
        {
            int l,r,num;
            cin >> l >> r >> num;
            modify(1,l,r,2*num);
        }
        else
        {
            int pos;
            cin >> pos;
            int l = 1, r = min(pos-1, n-pos), ans = 0;
//            cout << "("<< l << "," << r << "):" << ans << '\n';
            while(l <= r)
            {
                int mid = (l + r) / 2;
                if(queryl(1,pos-mid,pos-1) == queryr(1,pos+1,pos+mid)) l = mid + 1, ans = mid;
                else r = mid - 1;
//                cout << "("<< l << "," << r << "):" << ans << '\n';
            }
            cout << ans << '\n';
        }
    }
    return 0;
}

E. Dominating Point

我们发现,如果暴力枚举的话显然是 \(O(n^3)\) 的,但是因为是 \(01\) 传递状态,我们显然可以使用 \(bitset\) 加速,达到 \(O(\frac {n^3} {32})\)

但是也不知道怎么证明,反正无论如何构造都是远远跑不满的,实测可过

code
#include<bits/stdc++.h>
using namespace std;
const int NN = 5e3 + 8;
bitset<NN> A[NN];
bitset<NN> B[NN];
int n;

int main() {
	ios::sync_with_stdio(false),cin.tie(0); 
	cin >> n;
	for(int i = 0; i < n; ++i){
		string s;
		cin >> s;
		for(int j = 0; j < n; ++j){
			A[i][j] = B[j][i] = (s[j] == '1');
		} 
	}
	vector<int> ans;
	
	for(int v = 0; v < n; ++v){
		bool flag = 0;
		for(int w = 0; w < n; ++w){
			if(v == w) continue;
			if(A[v][w]) continue;
			if((A[v] & B[w]).any()) continue;
			flag = 1;
			break;
		}
		if(!flag) ans.push_back(v);
	}
	if(ans.size() < 3) cout << "NOT FOUND\n";
	else for(int i = 0; i < 3; ++i) cout << ans[i] + 1 << ' ';
}

G. An Easy Math Problem

我们首先只记录 \(p,q\) 互质是产生的 \(\frac p q\) 的值。

其次,我们对于 \(p\leq q\) 这个限制非常难受,于是我们就解散这个性质,最后让求出的答案 \(/2\) 即可

我们最后考虑怎么求答案:

我们记 \(n\) 因式分解后结果为:\(n = p_1^{r_1} \times p_2^{r_2}\times \dots p_k^{r_k}\)

我们对于每个质数和质数的次幂,我们把它们分给 \(p/q\) 的方案数为 \(2\times r_i + 1\)

所以最后的答案为 \(\frac {\prod_{i=1}^k (2\times r_i+1) - 1}{2} + 1\)\(p=1,q=1\) 以上方案中只会被算上一次,所以 \(/2\) 的时候需要单独拎出来)

code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int q;
ll n;
ll cnt[40],len;
void solve()
{
    len = 0;
    cin >> n;
    for(ll i = 2; i*i <= n; ++i)
    {
        if(n % i == 0)
        {
            cnt[++len] = 0;
            while(n % i == 0) n /= i, ++cnt[len];
        }
    }
    if(n != 1) cnt[++len] = 1;
    ll r = 1;
    for(int i = 1; i <= len; ++i)
    {
        r *= (2 * cnt[i]) + 1;
        // cout << cnt[i] << ' ';
    }
    r = (r + 1) / 2;
    cout << r << '\n';
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> q;
    while(q--)
    {
        solve();
    }
    return 0;
}

H. Elimination Series Once More

归并排序的时候,找到当前比赛区间中比这个数大的数的个数(需要被交换出去的数的个数)

如果比该数大的数的个数 \(\leq k\),并且有这么多小于它的数可以被换进来,那么就可以打到这么多轮次

code
#include <bits/stdc++.h>
using namespace std;
const int NN = (1 << 20) + 8;
int n,k;
struct Perm
{
    int num;
    int id;
    bool operator < (const Perm &x) const
    {
        return num < x.num;
    }
}a[NN];
int now[NN];
int ans[NN];
void solve(int l,int r,int dep)
{
    // printf("in:(%d,%d,%d)\n",l,r,dep);
    if(l == r) return;
    int mid = (l + r) / 2;
    solve(l,mid,dep-1); solve(mid+1,r,dep-1);
    int i = l, j = mid+1;
    for(; i <= mid; ++i)
    {
        while(j != r+1 && a[j].num < a[i].num)
        {
            now[a[j].id] += mid - i + 1;
            if(now[a[j].id] <= k && (a[j].num >> dep) > 0) ans[a[j].id] = dep;
            ++j;
        }
        now[a[i].id] += r-j+1;
        if(now[a[i].id] <= k && (a[i].num >> dep) > 0) ans[a[i].id] = dep;
    }
    for(; j <= r; ++j)
    {
        if(now[a[j].id] <= k && (a[j].num >> dep) > 0) ans[a[j].id] = dep;
    }
    // for(int i = 1; i <= (1 << n); ++i)
    // {
    //     printf("%d ",now[i]);
    // }
    // printf("\n");
    sort(a+l,a+r+1);
}
int main()
{
    // ios::sync_with_stdio(false),cin.tie(0);
    cin >> n >> k;
    for(int i = 1; i <= (1<<n); ++i) cin >> a[i].num, a[i].id = i;
    solve(1,(1<<n),n);
    for(int i = 1; i <= (1<<n); ++i) cout << ans[i] << ' ';
    return 0;
}

N. Python Program

模拟题,不改了

快读读出所有数即可。

posted @ 2025-10-21 11:33  ricky_lin  阅读(44)  评论(0)    收藏  举报