ACL Contest 1 solution

前言

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

A Reachable Towns

n n n个点,每个点有两个参数 ( x i , y i ) (x_i,y_i) (xi,yi).
i , j i,j i,j有连边,当且仅当 x i < x j , y i < y j    o r    x i > x j , y i > y j x_i<x_j,y_i<y_j~~or~~x_i>x_j,y_i>y_j xi<xj,yi<yj  or  xi>xj,yi>yj.
求每个点的可达点数目.

显然这是一张无向图,我们数只要求连通块的大小即可.
我们先按 x x x排序(使得 x i = i x_i=i xi=i),然后对于每个点 i i i考虑连边.
如果存在 j < i , y j < y i j<i,y_j<y_i j<i,yj<yi那么 i , j i,j i,j就在同一个连通块.
由此我们可以发现,我们只要记录一个连通块的最小 y y y,用一个单调栈维护一下 y y y单调下降即可.
并查集用按秩合并+路径压缩,可以总复杂度为 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 + 1 i,i+1 i,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

求最小的 k k k满足 n ∣ ( 1 + 2 + . . . + k ) n|(1+2+...+k) n(1+2+...+k).( n ≤ 1 e 15 n\le 1e15 n1e15)

转化得: 2 n ∣ k ( k + 1 ) 2n|k(k+1) 2nk(k+1).
因为 k , k + 1 k,k+1 k,k+1互质,所以 2 n 2n 2n中的一些质因子由 k k k处理,其他由 k + 1 k+1 k+1处理.
s ∣ 2 n , gcd ⁡ ( s , t = 2 n / 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 = f s k=fs k=fs,因为 t ∣ ( k + 1 ) t|(k+1) t(k+1),所以可得:
f s + 1 ≡ 0 ( m o d    t ) → f s ≡ − 1 ( m o d    t ) fs+1\equiv 0(\mod t)\rightarrow fs\equiv -1(\mod t) fs+10(modt)fs1(modt)
我们只要求 − s -s s的逆元即可.

因为总质因子个数 p ≤ 14 p\le 14 p14,所以我们用 2 p 2^p 2p暴力枚举即可.
总复杂度为 O ( 2 p log ⁡ 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

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

a 1 < a 2 < . . . < a m a_1<a_2<...<a_m a1<a2<...<am为一组字典序最小的解(贪心地从 L L L开始每次取最小的后继), b 1 < b 2 < . . . < b m b_1<b_2<...<b_m b1<b2<...<bm为字典序最大的一组解(从 R R R开始贪心取最大后继).

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

那么 a n s = ∑ i = 1 m b i − a i + 1 ans=\sum_{i=1}^m b_i-a_i+1 ans=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

给定一个 n n n的排列 p p p.然后令 i = 1... n − k i=1...n-k i=1...nk进行以下操作:
r a n d o m _ s h u f f l e ( p + i , p + k + 1 ) random\_shuffle(p+i,p+k+1) random_shuffle(p+i,p+k+1).
问最后的期望逆序对数量.
n ≤ 2 e 5 n\le 2e5 n2e5.

对一个序列进行两次 r a n d o m _ s h u f f l e random\_shuffle random_shuffle等价于进行一次.
所以题目可以转化为:
维护一个大小为 k k k的多重集 s s s,初始 s = { p 1 , p 2 . . . . p k } s=\{p_1,p_2....p_k\} s={p1,p2....pk}
然后对于每一个 i ≤ n − k + 1 i\le n-k+1 ink+1,选出一个数为 p i ′ p_i' pi,然后加入 p i + k p_{i+k} pi+k.
最后乱排 s s s,放入序列.

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

固定 j j j,则总代价为 ∑ i = 1 j − 1 [ a i > a j ] ( 1 − p f j − f i 2 )    +    [ a i < a j ] p f j − f i 2 \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}}2 i=1j1[ai>aj](12pfjfi)  +  [ai<aj]2pfjfi.用树状数组维护 1 p f i \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、付费专栏及课程。

余额充值