ACL Contest 1 solution

本文提供了四道算法竞赛题目的解答思路及代码实现,包括图论、数论、费用流及区间查询等问题,采用C++语言编写。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

比赛时,只做出前3题,上了一点点分…(写得不好,请轻喷).

A Reachable Towns

nnn个点,每个点有两个参数(xi,yi)(x_i,y_i)(xi,yi).
i,ji,ji,j有连边,当且仅当xi<xj,yi<yj  or  xi>xj,yi>yjx_i<x_j,y_i<y_j~~or~~x_i>x_j,y_i>y_jxi<xj,yi<yj  or  xi>xj,yi>yj.
求每个点的可达点数目.

显然这是一张无向图,我们数只要求连通块的大小即可.
我们先按xxx排序(使得xi=ix_i=ixi=i),然后对于每个点iii考虑连边.
如果存在j<i,yj<yij<i,y_j<y_ij<i,yj<yi那么i,ji,ji,j就在同一个连通块.
由此我们可以发现,我们只要记录一个连通块的最小yyy,用一个单调栈维护一下yyy单调下降即可.
并查集用按秩合并+路径压缩,可以总复杂度为O(nα(n))O(n\alpha(n))O(nα(n)).

int x[N],y[N],n,sta[N],top,fa[N],sz[N],f[N];
int get(int x) {return fa[x] == x?x:fa[x]=get(fa[x]);}
 
int main() {
    qr(n);
    for(int i=1;i<=n;i++) qr(x[i]),qr(y[x[i]]);
    for(int i=1;i<=n;i++) {
        fa[i]=i; sz[i]=1; f[i]=y[i];
        while(top&&f[sta[top]]<y[i]) {
            int t=get(sta[top--]);
            if(t^i) fa[t]=i,sz[i] += sz[t],f[i]=min(f[i],f[t]);
        }
        sta[++top]=i;
    }
    for(int i=1;i<=n;i++) pr2(sz[get(x[i])]);
    return 0;
}

由上面的实现可以发现一个连通块的编号连续(可以用归纳证明)
所以我们考虑什么情况下i,i+1i,i+1i,i+1在不同连通块.
要使得[1,i],(i,n][1,i],(i,n][1,i],(i,n]没有边,必须让[1,i][1,i][1,i]的每个点都大于[i+1,n][i+1,n][i+1,n]的任意点.
[1,i][1,i][1,i]覆盖值域[n−i+1,n][n-i+1,n][ni+1,n].
总复杂度为O(n)O(n)O(n).

int n,x[N],y[N],sz[N],last,p;
bool v[N];

int main() {
    qr(n);
    for(int i=1;i<=n;i++) qr(x[i]),qr(y[x[i]]);
    p=n; last=1;
    for(int i=1;i<=n;i++) {
        v[y[i]]=1;
        while(v[p]) p--;
        if(n-p==i) {
            int s=i-last+1;
            while(last<=i) sz[last++]=s;
        }
    }
    for(int i=1;i<=n;i++) pr2(sz[x[i]]);
    return 0;
}

B Sum is Multiple

求最小的kkk满足n∣(1+2+...+k)n|(1+2+...+k)n(1+2+...+k).(n≤1e15n\le 1e15n1e15)

转化得:2n∣k(k+1)2n|k(k+1)2nk(k+1).
因为k,k+1k,k+1k,k+1互质,所以2n2n2n中的一些质因子由kkk处理,其他由k+1k+1k+1处理.
s∣2n,gcd⁡(s,t=2n/s)=1,s∣k,t∣(k+1)s|2n,\gcd(s,t=2n/s)=1,s|k,t|(k+1)s2n,gcd(s,t=2n/s)=1,sk,t(k+1).
k=fsk=fsk=fs,因为t∣(k+1)t|(k+1)t(k+1),所以可得:
fs+1≡0(mod  t)→fs≡−1(mod  t)fs+1\equiv 0(\mod t)\rightarrow fs\equiv -1(\mod t)fs+10(modt)fs1(modt)
我们只要求−s-ss的逆元即可.

因为总质因子个数p≤14p\le 14p14,所以我们用2p2^p2p暴力枚举即可.
总复杂度为O(2plog⁡n)O(2^p \log n)O(2plogn).

ll n,ans;
ll p[N],tot,c[1<<16];

void exgcd(ll &x,ll &y,ll a,ll b) {
	if(!a) {x=0; y=1; return ;}
	exgcd(y,x,b%a,a); x -= b/a*y;
}
ll inv(ll a,ll b) {
	a=(2*b-a)%b;
	ll x,y; exgcd(x,y,a,b);
	return (x%b+b)%b;
}

void div(ll x) {
	for(ll i=2;i*i<=x;i++) if(x%i == 0) {
		p[tot]=x;
		while(x%i==0) x/=i;
		p[tot++] /= x;
	}
	if(x>1) p[tot++]=x;
}

int main() {
	qr(n); div(n*2); c[0]=1;
	for(int i=0;i<tot;i++) c[1<<i]=p[i];
	if(n==1) puts("1"),exit(0);
	if(n&1) ans=n-1;
	else ans=n;
	for(int i=1;i+1<(1<<tot);i++) {
		c[i]=c[i&(i-1)]*c[i&(-i)];
		ll x=c[i],y=2*n/x;
		ans=min(ans,x*inv(x,y));
	}
	pr2(ans);
}

C Moving Pieces

裸费用流.

#include<bits/stdc++.h>
#define fi first
#define se second
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pb push_back
#define IT iterator
#define vi vector<int>
#define TP template<class o>
#define SZ(a) ((int)a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=55,size=1<<20,mod=998244353,inf=2e9;

//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {
    char c=gc; x=0; int f=1;
    while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
    while(isdigit(c)) x=x*10+c-'0',c=gc;
    x*=f;
}
template<class o> void qw(o x) {
    if(x/10) qw(x/10);
    putchar(x%10+'0');
}
template<class o> void pr1(o x) {
    if(x<0)x=-x,putchar('-');
    qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
    if(x<0)x=-x,putchar('-');
    qw(x); putchar('\n');
}

int n,m,ans,st,ed;
char s[N][N];

int id(int x,int y) {return (x-1)*m+y;} 

struct edge{int y,next,c,d; } a[N*N*8]; int len=1,last[N*N];
void ins(int x,int y,int c,int d) {a[++len]=(edge){y,last[x],c,d}; last[x]=len;}
void add(int x,int y,int c,int d) {ins(x,y,c,d); ins(y,x,0,-d);}

int d[N*N],q[N*N],l,r,pre[N*N];
bool vis[N*N];

void EK() {
	while(233) {
		memset(d,-63,sizeof(d)); 
		l=1; r=2; q[l]=st; pre[ed]=0; d[st]=0; vis[st]=1;
		while(l!=r) {
			int x=q[l++]; if(l==N*N) l=1; vis[x]=0;
			for(int k=last[x],y;k;k=a[k].next) {
				y=a[k].y;
				if(a[k].c&&d[x]+a[k].d>d[y]) {
					d[y]=d[x]+a[k].d;
					pre[y]=k^1;
					if(!vis[y]) {
						q[r++]=y;
						if(r==N*N) r=1;
					}
				}
			}
		}
		if(!pre[ed]) return ;
		ans += d[ed]; int x=ed,k;
		while(x^st) {
			k=pre[x];
			a[k].c++;
			a[k^1].c--;
			x=a[k].y;
		}
	}
}

void solve() {
	qr(n); qr(m); st=n*m+1; ed=st+1;
	for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++) {
			if(s[i][j]!='#') {
				if(i<n&&s[i+1][j]!='#') add(id(i,j),id(i+1,j),inf,1);
				if(j<m&&s[i][j+1]!='#') add(id(i,j),id(i,j+1),inf,1);
				add(id(i,j),ed,1,0);
				if(s[i][j]=='o') add(st,id(i,j),1,0);
			}
		}
	EK(); pr2(ans);
}

int main() {
	solve();
}


D Keep Distances

给定一个长度为nnn递增数组xxx和参数kkk.
一个集合sss合法当且仅当任意不同元素i,j,∣xi−xj∣≥ki,j,|x_i-x_j|\ge ki,j,xixjk.
qqq组询问,每组询问有参数L,RL,RL,R.
此时每个元素必须∈[L,R]\in[L,R][L,R].
求最大集合的并集大小.

a1<a2<...<ama_1<a_2<...<a_ma1<a2<...<am为一组字典序最小的解(贪心地从LLL开始每次取最小的后继),b1<b2<...<bmb_1<b_2<...<b_mb1<b2<...<bm为字典序最大的一组解(从RRR开始贪心取最大后继).

那么一定有a1≤b1<a2≤b2<a3≤b3<....<am≤bma_1\le b_1<a_2\le b_2<a_3\le b_3<....<a_m\le b_ma1b1<a2b2<a3b3<....<ambm.
假设存在bi≥ai+1b_i\ge a_{i+1}biai+1,那么有(a1,a2...ai+1,bi+1,..bm)(a_1,a_2...a_{i+1},b_{i+1},..b_m)(a1,a2...ai+1,bi+1,..bm)一组大小为m+1m+1m+1的解,与最大性矛盾.
现在证明∀bi<p<ai+1\forall b_i<p<a_{i+1}bi<p<ai+1,ppp为集合中的元素时,集合不能为最大.
因为左边至多取i−1i-1i1,右边至多取m−i−1m-i-1mi1,所以最大取到m−1m-1m1.

那么ans=∑i=1mbi−ai+1ans=\sum_{i=1}^m b_i-a_i+1ans=i=1mbiai+1.

#include<bits/stdc++.h>
#define fi first
#define se second
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pb push_back
#define IT iterator 
#define vi vector<int>
#define TP template<class o>
#define SZ(a) ((int)a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=2e5+10,size=1<<20,mod=998244353,inf=2e9;

//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {
    char c=gc; x=0; int f=1;
    while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
    while(isdigit(c)) x=x*10+c-'0',c=gc;
    x*=f;
}
template<class o> void qw(o x) {
    if(x/10) qw(x/10);
    putchar(x%10+'0');
}
template<class o> void pr1(o x) {
    if(x<0)x=-x,putchar('-');
    qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
    if(x<0)x=-x,putchar('-');
    qw(x); putchar('\n');
}

int n,m,q,lg,x[N],r[N][20],l[N][20];
ll sl[N][20],sr[N][20];

int main() {
    qr(n); qr(m);
    for(int i=1;i<=n;i++) qr(x[i]);
    for(int i=1;i<=n;i++) {
        r[i][0]=r[i-1][0];
        while(r[i][0]<=n&&x[r[i][0]]-x[i]<m) r[i][0]++;
        sr[i][0]=r[i][0]-1;
    }
    l[n+1][0]=n;
    for(int i=n;i;i--) {
        l[i][0]=l[i+1][0];
        while(l[i][0]&&x[i]-x[l[i][0]]<m) l[i][0]--;
        sl[i][0]=l[i][0];
    }
    for(int i=0;i<20;i++) r[n+1][i]=n+1;
    for(int j=1;(1<<j)<=n;j++,lg++)
        for(int i=1;i<=n;i++) {
            l[i][j]=l[l[i][j-1]][j-1];
            sl[i][j]=sl[i][j-1]+sl[l[i][j-1]][j-1];
            r[i][j]=r[r[i][j-1]][j-1];
            sr[i][j]=sr[i][j-1]+sr[r[i][j-1]][j-1];
        }
    qr(q); while(q--) {
        int L,R; qr(L); qr(R); ll ans=0;
        ans -= L-1;
        for(int v=L,i=lg;i>=0;i--)
            if(r[v][i]<=R) 
				ans -= sr[v][i],v=r[v][i];
        ans += R; 
        for(int v=R,i=lg;i>=0;i--)
            if(l[v][i]>=L) 
				ans += sl[v][i],v=l[v][i];
        pr2(ans);
    }
    return 0;
}

E Shuffle Window

给定一个nnn的排列ppp.然后令i=1...n−ki=1...n-ki=1...nk进行以下操作:
random_shuffle(p+i,p+k+1)random\_shuffle(p+i,p+k+1)random_shuffle(p+i,p+k+1).
问最后的期望逆序对数量.
n≤2e5n\le 2e5n2e5.

对一个序列进行两次random_shufflerandom\_shufflerandom_shuffle等价于进行一次.
所以题目可以转化为:
维护一个大小为kkk的多重集sss,初始s={p1,p2....pk}s=\{p_1,p_2....p_k\}s={p1,p2....pk}
然后对于每一个i≤n−k+1i\le n-k+1ink+1,选出一个数为pi′p_i'pi,然后加入pi+kp_{i+k}pi+k.
最后乱排sss,放入序列.

fi=max⁡(i−k,0)f_i=\max(i-k,0)fi=max(ik,0)表示最早加入集合的时间.
我们考虑i<ji<ji<j的数对相对关系交换的概率.
p=k−1kp=\dfrac{k-1}{k}p=kk1表示一个多重集内的数活一轮的概率.
那么我们要先把iii移动到jjj加入多重集的时候,总概率为pfj−fip^{f_j-f_i}pfjfi.
然后在一个多重集的时候,有12\dfrac 1221的概率先后顺序发生改变.
所以总体来看i<ji<ji<j,最后发生位置相对变化的概率为pfj−fi2\dfrac {p^{f_j-f_i}}22pfjfi.

固定jjj,则总代价为∑i=1j−1[ai>aj](1−pfj−fi2)  +  [ai<aj]pfj−fi2\sum_{i=1}^{j-1} [a_i>a_j](1-\dfrac {p^{f_j-f_i}}2)~~+~~[a_i<a_j]\dfrac {p^{f_j-f_i}}2i=1j1[ai>aj](12pfjfi)  +  [ai<aj]2pfjfi.用树状数组维护1pfi\dfrac 1{p^{f_i}}pfi1即可.

#include<bits/stdc++.h>
#define fi first
#define se second
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pb push_back
#define IT iterator 
#define vi vector<int>
#define TP template<class o>
#define SZ(a) ((int)a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=2e5+10,size=1<<20,mod=998244353,inf=2e9;

//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {
    char c=gc; x=0; int f=1;
    while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
    while(isdigit(c)) x=x*10+c-'0',c=gc;
    x*=f;
}
template<class o> void qw(o x) {
    if(x/10) qw(x/10);
    putchar(x%10+'0');
}
template<class o> void pr1(o x) {
    if(x<0)x=-x,putchar('-');
    qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
    if(x<0)x=-x,putchar('-');
    qw(x); putchar('\n');
}

ll p[N],np[N],n,k,s,cnt,ans;
int c[N],a[N];

TP void upd(o &x,int y) {x += y; if(x>=mod) x -= mod;}
void add(int x,int y) {upd(s,y); for( ;x<=n;x += x&-x) upd(c[x],y); }
int ask(int x) {ll y=0; for( ; x;x -= x&-x) y += c[x]; return y%mod; }
void clear() {memset(c+1,0,sizeof(int)*n); s=0;}

ll mult(ll a,ll b,ll p) {
    a=(a%p+p)%p; b=(b%p+p)%p;
    ll c=(ld)a*b/p;
    return a*b-c*p;
}
ll gcd(ll a,ll b) {return !a?b:gcd(b%a,a);}
ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
ll power(ll a,ll b=mod-2) {
    ll c=1;
    while(b) {
        if(b&1) c=c*a%mod;
        b /= 2; a=a*a%mod;
    }
    return c;
}
ll Power(ll a,ll b=mod-2) {
    ll c=1;
    while(b) {
        if(b&1) c=mult(c,a,mod);
        b /= 2; a=mult(a,a,mod);
    }
    return c;
}

int main() {
    qr(n); qr(k);
    for(int i=1;i<=n;i++) 
		qr(a[i]),add(a[i],1),cnt += i-ask(a[i]);
    p[0]=np[0]=1; p[1]=(k-1)*power(k)%mod; np[1]=power(p[1]); clear();
    for(int i=2;i<=n;i++) p[i]=p[i-1]*p[1]%mod,np[i]=np[i-1]*np[1]%mod;
    for(int i=1;i<=n;i++) {
        int f=max(i-k,0LL);
        ans += p[f]*(2*ask(a[i])-s+mod)%mod;
        add(a[i],np[f]);
    }
    pr2((cnt+ans%mod*(mod+1)/2%mod)%mod); return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Infinite_Jerry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值