ACL Contest 1 题解(A-E)

89 篇文章 1 订阅

ACL Contest 1 题解(A-E)

A

问题可以转换一下:

有一个排列 p 1 . . . p n p_1...p_n p1...pn,如果 p i > p j , i > j p_i>p_j,i>j pi>pj,i>j i i i j j j连一条边。如果 p i < p j , i < j p_i<p_j,i<j pi<pj,i<j i i i j j j连一条边。

可以发现如果 p 1 . . . p i p_1...p_i p1...pi n . . . n − i + 1 n...n-i+1 n...ni+1的排列,则不存在边 j ↔ k , ( j ≤ i , k > i ) j\leftrightarrow k,(j\leq i,k>i) jk,(ji,k>i)

然后对于每一个联通块计算答案即可。

时间复杂度: O ( n ) O(n) O(n)

/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
const int MAXN=200000+20;
int fa[MAXN],is[MAXN],siz[MAXN];
int n;
mp cit[MAXN];
int root(int x){
	if(fa[x]==x) return x;
	return fa[x]=root(fa[x]);
}
void merge(int u,int v){
	if(root(u)==root(v)) return ;
	siz[root(v)]+=siz[root(u)];
	fa[root(u)]=root(v);
}
int main(){
	fastio;
	R(n);
	rb(i,1,n)
		fa[i]=i,siz[i]=1;
	rb(i,1,n){
		R2(cit[i].FIR,cit[i].SEC);
		is[i]=cit[i].FIR;
	}
	sort(cit+1,cit+1+n);
	int las=0,mi=INF;
	rb(i,1,n){
		check_min(mi,cit[i].SEC);
		if(mi==n-i+1){
			rb(j,las+1,i)
				if(j!=las+1)
					merge(j,j-1);
			las=i;
		}
	}
	rb(i,1,n)  cout<<siz[root(is[i])]<<endl;
	return 0;
}

B

K K K的质因子集合为 P 1 P_1 P1, ( k + 1 ) (k+1) (k+1)的为 P 2 P_2 P2, N N N的为 P N P_N PN

满足 ∀ x ∈ P N ⇒ x ∈ P 1   o r   x ∈ P 2 \forall x\in P_N\Rightarrow x\in P_1 \ or\ x\in P_2 xPNxP1 or xP2

A = ∏ x , ( x ∈ P 1 ∩ P N ) A=\prod x,(x\in P_1 \cap P_N) A=x,(xP1PN), B = ∏ x , ( x ∈ P 2 ∩ P N ) B=\prod x,(x\in P_2 \cap P_N) B=x,(xP2PN)

k = a ∗ A , k + 1 = b ∗ B ⇔ a ∗ A + 1 = b ∗ B ⇔ − a ∗ A + b ∗ B = 1 k=a*A,k+1=b*B\Leftrightarrow a*A+1=b*B\Leftrightarrow -a*A+b*B=1 k=aA,k+1=bBaA+1=bBaA+bB=1

可以发现可以枚举 A , B A,B A,B,然后 E x G c d ExGcd ExGcd算出最小的 − a -a a就行了。

时间复杂度 O ( n ) O(\sqrt n) O(n )

Code:

/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
LL n;
LL exGcd(LL a,LL b,LL &x,LL &y)
{
    if(b==0)
    {
        x = 1;
        y = 0;
        return a;
    }
    LL r = exGcd(b,a%b,x,y);
    LL t = x; x = y;
    y = t-a/b*y;
    return r;
}
int main(){
	fastio;
	cin>>n;
	n<<=1;
	LL rest=n-1;
	for(LL i=1;i*i<=n;i++){
		LL a,b;
		if(n%i==0){
			a=i;
			b=n/i;
			if(__gcd(a,b)!=1) continue;
			LL x,y;
			exGcd(a,b,x,y);
			if(x>=0){
				LL t=(-x+b)/b;
				x-=t*b;
				y+=t*a;
			}
			if(y<=0){
				LL t=(-y+a)/a;
				x-=t*b;
				y+=t*a;
			}
			check_min(rest,-1ll*a*x);
			// a*x=b*y+1
			// min(a*x)
			swap(a,b);
			exGcd(a,b,x,y);
			if(x>=0){
				LL t=(-x+b)/b;
				x-=t*b;
				y+=t*a;
			}
			if(y<=0){
				LL t=(-y+a)/a;
				x-=t*b;
				y+=t*a;
			}
			check_min(rest,-1ll*a*x);
		}
	}
	cout<<rest<<endl;
	return 0;
}

C

直接连边,跑最小费用最大流。

时间复杂度 O ( ? ? ? ) O(???) O(???)

/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
const int GRAPH_SIZE= 50*50+100+20;
int pin[GRAPH_SIZE],dep[GRAPH_SIZE],s=0,t=GRAPH_SIZE-1;
struct EDGE{
	int u,v,c,cos;
};
vector<EDGE> e;
vector<int> each[GRAPH_SIZE];
int maxflow,mincost;
int flow[GRAPH_SIZE];
int dis[GRAPH_SIZE],inq[GRAPH_SIZE],las[GRAPH_SIZE];
bool spfa(){
	memset(inq,0,sizeof(inq));
	memset(dis,63,sizeof(dis));
	flow[s]=INF;
	dis[s]=0;
	queue<int> q;
	q.push(s);
	inq[s]=1;
	while(!q.empty()){
		int now=q.front();
		q.pop();
		inq[now]=0;
		for(auto it:each[now]){
			int to,f,c;
			to=e[it].v;
			f=e[it].c;
			c=e[it].cos;
			if(f<=0) continue;
			if(dis[to]>dis[now]+c){
				dis[to]=dis[now]+c;
				las[to]=it;
				flow[to]=min(flow[now],f);
				if(!inq[to]) q.push(to);
				inq[to]=1;		
			}
		}
	}
	return dis[t]!=INF;
}
void KM(){
	while(spfa()){
		maxflow+=flow[t];
		mincost+=dis[t]*flow[t];
		int now=t;
		while(now!=s){
			e[las[now]].c-=flow[t];
			e[las[now]^1].c+=flow[t];
			now=e[las[now]].u;
		}
	}
}
void make_edge(int U,int V,int C,int COS){
	EDGE tmp;
	tmp.u=U;
	tmp.v=V;
	tmp.c=C;
	tmp.cos=COS;
	e.PB(tmp);
	each[U].PB(e.size()-1);
	swap(tmp.u,tmp.v);
	tmp.c=0;
	tmp.cos=-COS;
	e.PB(tmp);
	each[V].PB(e.size()-1);
}
int n,m;
string maze[51];
int walk[2][2]={{1,0},{0,1}};
bool check(int x,int y){
	if(x<=0||y<=0||x>n||y>m) return 0;
	if(maze[x][y]=='#') return 0;
	return 1;
}
int main(){
	fastio;
	R2(n,m);
	rb(i,1,n) R(maze[i]);
	rb(i,1,n) maze[i]='^'+maze[i];
	rb(i,1,n)
		rb(j,1,m){
			int id=(i-1)*m+j;
			if(maze[i][j]=='o')
			make_edge(s,id,1,0);
			make_edge(id,t,1,0);
			rep(k,2)
			{
				int nx,ny;
				nx=i+walk[k][0];
				ny=j+walk[k][1];
				if(check(nx,ny)){
					make_edge(id,(nx-1)*m+ny,INF,-1);
				}
			}
		}
	KM();
	cout<<-mincost<<endl;
	return 0;
}

D

可以发现最优的答案一定是包含 L L L的。

然后我们可以从 L L L开始,倍增找到答案。

对于一种答案,它的一些地方可以往后移动,移到最后就是从 R R R往前跑的答案。

然后用倍增维护答案的下标和就行了。

时间复杂度 O ( n ∗ log ⁡ 2 ( n ) ) O(n*\log_2(n)) O(nlog2(n))

/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
int n,k,x[200000+20],q,f[200000+20][19],ff[200000+20][19];
LL s[200000+20][19],ss[200000+20][19];
LL solve(int l,int r){
	int can=0;
	int now=l;
	rl(i,18,0)
		if(f[now][i]<=r) now=f[now][i],can|=1<<i;
	if(can==0) return r-l+1;
	LL one,two;
	one=two=0;
	now=l;
	rl(i,18,0)
		if((can>>i)&1){
			one+=s[now][i];
			now=f[now][i];	
		}
	now=r;
	rl(i,18,0)
		if((can>>i)&1){
			two+=ss[now][i];
			now=ff[now][i];
		}
	one+=l;
	two+=r;
	return two-one+can+1;
}
int main(){
	fastio;
	R2(n,k);
	rb(i,1,n)
		R(x[i]);
	rb(i,1,n+1)
		fill(f[i],f[i]+19,n+1);
	int is=n+1;
	rl(i,n,1){
		while(x[is-1]-x[i]>=k){
			is--;
		}
		f[i][0]=is;
		s[i][0]=is;
	}
	is=0;
	rb(i,1,n){
		while(x[i]-x[is+1]>=k){
			is++;
		}
		ff[i][0]=is;
		ss[i][0]=is;
	}
	rb(i,1,18)
		rb(j,1,n)
			f[j][i]=f[f[j][i-1]][i-1],ff[j][i]=ff[ff[j][i-1]][i-1],s[j][i]=s[f[j][i-1]][i-1]+s[j][i-1],ss[j][i]=ss[ff[j][i-1]][i-1]+ss[j][i-1];
	R(q);
	while(q--){
		int l,r;
		R2(l,r);
		cout<<solve(l,r)<<endl;
	}
	return 0;
}
/** 程序框架:
  * f_{i,j}=f_{f_{i,j-1},j-1}\\
  * s_{i,j}=s_{i,j-1}+s_{f_{i,j-1},j-1}
  * 
  * 
  *
  **/

E

可以发现我们要求出两个被交换的概率,设为 P ( i , j ) , ( i < j ) P(i,j),(i<j) P(i,j),(i<j)

题目可以转换:

你一开始有 k k k张牌, a 1 . . a k a_1..a_k a1..ak,然后扔掉一张,再拿一张。。。这样一共进行 n − k + 1 n-k+1 nk+1次.

显然如果 i i i要在 j j j后面扔掉,则拿到 i i i到拿到 j j j的这段里不能把 i i i扔掉。 i i i j j j都拿到之后谁先扔掉的概率就是 1 2 \frac{1}2 21

然后bit优化一下就行了。

时间复杂度:

O ( n ∗ log ⁡ 2 ( n ) ) O(n*\log_2(n)) O(nlog2(n))

/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
const int MOD=998244353;
LL quick(LL A,LL B){
	if(B==0) return 1;
	LL  tmp=quick(A,B>>1);
	tmp*=tmp;
	tmp%=MOD;
	if(B&1)
		tmp*=A,tmp%=MOD;
 	return tmp;
}
void add(int & A,int B){
	A+=B;
	if(A>=MOD) A-=MOD;
}
int inv(int A){return quick(A,MOD-2);}
int x[200000+20];
int n,k;
const int MAXN=200000;
struct BIT{
	void add(int & A,int B){
		A+=B;
		if(A>=MOD) A-=MOD;
	}
	int bit[MAXN+10]={0};
	int sum(int i){
		int s=0;
		while(i>0){
			add(s,bit[i]);
			i-=i&(-i);
		}
		return s;
	}
	void modi(int i,int x=1){
		while(i<=n){
			add(bit[i],x);
			i+=i&(-i);	
		}
	}
}sum,cnt;
int main(){
	fastio;
	int rest=0;
	cin>>n>>k;
	rb(i,1,n) R(x[i]);
	int p=1ll*(k-1)*inv(k)%MOD;
	rl(i,n,1){
		int fi=max(0,i-k);
		int tmp;
		tmp=sum.sum(n)-sum.sum(x[i]);
		if(tmp<0) tmp+=MOD;
		tmp=1ll*tmp*inv(quick(p,fi))%MOD;
		add(rest,tmp);
		
		
		
		tmp=sum.sum(x[i]-1);
		int cn=cnt.sum(x[i]-1);
		tmp=1ll*tmp*inv(quick(p,fi))%MOD;
		cn-=tmp;
		if(cn<0) cn+=MOD;
		add(rest,cn);
		
		
		cnt.modi(x[i]);
		sum.modi(x[i],1ll*inv(2)*quick(p,fi)%MOD);
		
//		cout<<rest<<" "<<p<<endl;
	}
	cout<<rest<<endl;
	return 0;
}
/** 程序框架:
  *
  *
  *
  *
  **/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值