前言
比赛时,只做出前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]
[n−i+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 n≤1e15)
转化得:
2
n
∣
k
(
k
+
1
)
2n|k(k+1)
2n∣k(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)
s∣2n,gcd(s,t=2n/s)=1,s∣k,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+1≡0(modt)→fs≡−1(modt)
我们只要求
−
s
-s
−s的逆元即可.
因为总质因子个数
p
≤
14
p\le 14
p≤14,所以我们用
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,∣xi−xj∣≥k.
有 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
a1≤b1<a2≤b2<a3≤b3<....<am≤bm.
假设存在
b
i
≥
a
i
+
1
b_i\ge a_{i+1}
bi≥ai+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
i−1,右边至多取
m
−
i
−
1
m-i-1
m−i−1,所以最大取到
m
−
1
m-1
m−1.
那么 a n s = ∑ i = 1 m b i − a i + 1 ans=\sum_{i=1}^m b_i-a_i+1 ans=∑i=1mbi−ai+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...n−k进行以下操作:
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 n≤2e5.
对一个序列进行两次
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
i≤n−k+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(i−k,0)表示最早加入集合的时间.
我们考虑
i
<
j
i<j
i<j的数对相对关系交换的概率.
设
p
=
k
−
1
k
p=\dfrac{k-1}{k}
p=kk−1表示一个多重集内的数活一轮的概率.
那么我们要先把
i
i
i移动到
j
j
j加入多重集的时候,总概率为
p
f
j
−
f
i
p^{f_j-f_i}
pfj−fi.
然后在一个多重集的时候,有
1
2
\dfrac 12
21的概率先后顺序发生改变.
所以总体来看
i
<
j
i<j
i<j,最后发生位置相对变化的概率为
p
f
j
−
f
i
2
\dfrac {p^{f_j-f_i}}2
2pfj−fi.
固定 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=1j−1[ai>aj](1−2pfj−fi) + [ai<aj]2pfj−fi.用树状数组维护 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;
}