AtCoder Regular Contest 125

传送门

A − D i a l U p A-Dial Up ADialUp 贪 心 贪心

首先当 b b b a a a没有的元素的时候显然无解,否则我们可以找到离 a 1 a_1 a1最近的一个 ! a 1 !a_1 !a1,让后交替着来构造 b b b即可。

int n,m;
int a[N],b[N];

int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);

	int ans=0;
	scanf("%d%d",&n,&m);
	int dx,dy,dxx,dyy; dx=dy=dxx=dyy=0;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),dx+=a[i]==1,dy+=a[i]==0;
	for(int i=1;i<=m;i++) scanf("%d",&b[i]),dxx+=b[i]==1,dyy+=b[i]==0;
	dx=dx>0; dy=dy>0; dxx=dxx>0; dyy=dyy>0;
	if(dx<dxx||dy<dyy) {
		puts("-1");
		return 0;
	}
	ans+=m;
	int change=0;
	for(int i=2;i<=m;i++) if(b[i]!=b[i-1]) change++;
	if(a[1]==b[1]) {
		int cnt=INF;
		for(int i=2;i<=n;i++) if(a[i]!=a[1]) {
			cnt=min(cnt,i-1);
			cnt=min(cnt,n-i+1);
		}
		if(change) {
			ans+=cnt,ans+=change-1;
			if(cnt==INF) ans=-1;
		} 
	} else {
		int cnt=INF;
		for(int i=2;i<=n;i++) if(a[i]!=a[1]) {
			cnt=min(cnt,i-1);
			cnt=min(cnt,n-i+1);
		}
		ans+=cnt;
		ans+=change;
		if(change&&cnt==INF) ans=INF; 
	}
	printf("%d\n",ans);

	return 0;
}

B − S q u a r e s B-Squares BSquares 数 学 + 推 式 子 数学 + 推式子 +

考虑题面中的式子就是 x 2 − y = z 2 x^2-y=z^2 x2y=z2,其中 x ∈ [ 1 , 1 0 12 ] x\in [1,10^{12}] x[1,1012],也就是找 x 2 x^2 x2减去一个 [ 1 , 1 0 12 ] [1,10^{12}] [1,1012]内的数仍是一个平方数,由于得到的平方数都 ≤ x 2 \le x^2 x2,所以我们不妨先看看 ( x − 1 ) 2 (x-1)^2 (x1)2,化开就是 x 2 − ( 2 x − 1 ) x^2-(2x-1) x2(2x1),此时 y = 2 x − 1 y=2x-1 y=2x1,当 x x x取遍若干值的时候,也就是数列 1 , 3 , 5 , . . . 1,3,5,... 1,3,5,...,就是找这个数列中 ≤ n \le n n的数有多少个,这个是等差数列显然可以 O ( 1 ) O(1) O(1)算。让后再看 ( x − 2 ) 2 = x 2 − ( 4 x − 4 ) (x-2)^2=x^2-(4x-4) (x2)2=x2(4x4),此时 y = 4 x − 4 y=4x-4 y=4x4,数列就是 4 , 8 , 12 , . . . 4,8,12,... 4,8,12,...,依次找下去,可以发现就找以 i 2 i^2 i2开头, 2 i 2i 2i为公差的等比数列,有多少个 ≤ n \le n n的数,由于首项是平方的形式,显然可以 n \sqrt n n 枚举来算。

还可以将初始的式子移项,也就是 x 2 − z 2 = y x^2-z^2=y x2z2=y,由于 y ∈ [ 1 , n ] y\in [1,n] y[1,n],所以 x 2 − z 2 ≤ n x^2-z^2\le n x2z2n,换句话就是求 [ 0 , 1 , 4 , 9 , . . . , n 2 ] [0,1,4,9,...,n^2] [0,1,4,9,...,n2]这个数列中,有多少对不同的数差 ≤ n \le n n,将其差分之后得到 [ 1 , 3 , 5 , . . . ] [1,3,5,...] [1,3,5,...],也就是差分后的数列中有多少段非空区间的和 ≤ n \le n n,让后根据区间长度不同,可以分成 [ 1 , 3 , 5 , . . . ] , [ 4 , 8 , 12 , . . . ] , [ 9 , 15 , 21 , . . . ] [1,3,5,...],[4,8,12,...],[9,15,21,...] [1,3,5,...],[4,8,12,...],[9,15,21,...]这些区间跟上面的分析一样。

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

	LL n; cin>>n;
	LL ans=0;
	for(LL i=1;;i++) {
		LL a=i*i;
		if(a>n) break;
		LL d=i*2;
		ans+=(n-a)/d+1;
		ans%=mod;
	}
	cout<<ans<<endl;

C − L I S t o O r i g i n a l S e q u e n c e C-LIS to Original Sequence CLIStoOriginalSequence 贪 心 + 构 造 贪心 + 构造 +

考虑为什么这个东西一定有解,我们找个例子,比如 n = 5 , k = 2 , a 1 = 3 , a 2 = 5 n=5,k=2,a_1=3,a_2=5 n=5,k=2,a1=3,a2=5,我们可以构造 3 , 2 , 1 , 5 , 4 3,2,1,5,4 3,2,1,5,4,也就是在 a i , a i + 1 a_i,a_i+1 ai,ai+1之间以递减的方式填上 [ a i + 1 , a i + 1 − 1 ] [a_i+1,a_{i+1}-1] [ai+1,ai+11]这个区间中的数,显然这样一定可以构造出一个答案,下面考虑字典序最小。

我们上面实际上就是将其分成了 k k k块,每块只能选一个,我们依旧是按照这个思想来,考虑 a 1 a_1 a1之后最小的数能填多少?如果 a 1 a_1 a1不是 1 1 1的话,显然我们可以将 1 1 1放在 a 1 a_1 a1的后面,这个是最优的,一旦放了别的数,显然是没有这个优。但是我们放了 1 1 1之后,还能放别的吗?显然也是不能的,因为如果放了别的显然能将最长上升序列变长,所以我们就得到了这个题的做法,在每个数后面尝试放第一个小于它的并且没有被选择的数,最后的时候直接递减的输出没有选择的数即可。

int n,k;
int a[N],st[N];

int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);
	
	scanf("%d%d",&n,&k);
	for(int i=1;i<=k;i++) scanf("%d",&a[i]);
	int j=1;
	for(int i=1;i<k;i++) {
		printf("%d ",a[i]);
		st[a[i]]=1;
		while(st[j]) j++;
		if(j<a[i]) {
			st[j]=1;
			printf("%d ",j++);
		}
	}
	for(int i=n;i>=1;i--) if(!st[i]) printf("%d ",i);

	return 0;
}

D − U n i q u e S u b s e q u e n c e D-Unique Subsequence DUniqueSubsequence 树 状 数 组 + d p 树状数组+dp +dp

考虑 d p dp dp,设 f [ i ] f[i] f[i]表示前 i i i个能构成答案的子序列有多少,不难发现不合法的子序列是因为出现了相同的数,比如说 2 , 1 , 1 2,1,1 2,1,1 2 , 1 2,1 2,1这个子序列就是不合法的。所以需要将每个数分开来看,假设当前到了 i i i,上一次出现 a i a_i ai的位置是 p r e pre pre,那么我们显然可以从 [ p r e , i − 1 ] [pre,i-1] [pre,i1]这些位置转移过来,也就是在这些状态的子序列末尾加上 a i a_i ai,这样是合法的。对于 [ 1 , p r e − 1 ] [1,pre-1] [1,pre1]这些位置的状态显然是不能转移过来的,因为 f [ p r e ] f[pre] f[pre]这个位置已经包含了 [ 1 , p r e − 1 ] [1,pre-1] [1,pre1]这些位置后面加 a i a_i ai的方案数了,正如前面的例子 2 , 1 , 1 2,1,1 2,1,1 f [ 1 ] = 1 , f [ 2 ] = 2 f[1]=1,f[2]=2 f[1]=1,f[2]=2,对于 f [ 3 ] f[3] f[3]如果我们从 f [ 2 ] f[2] f[2]转移会得到 [ 2 , 1 , 1 ] , [ 1 , 1 ] [2,1,1],[1,1] [2,1,1],[1,1]这两个序列,如果我们再从 f [ 1 ] f[1] f[1]转移过来会得到 [ 2 , 1 ] [2,1] [2,1]这个序列,这个显然不合法,应该舍去。

经过上面的讨论,我们需要实现区间求和以及单点修改的数据结构,显然树状数组即可胜任。

注意某个数字第一次出现的时候需要加一,因为可能只选他一个。

int n;
int a[N];
int pre[N],last[N];
LL tr[N];

void add(int x,LL c) {
	c+=mod; c%=mod;
	for(int i=x;i<=n;i+=lowbit(i)) (tr[i]+=c)%=mod;
}

LL sum(int x) {
	LL ans=0;
	for(int i=x;i;i-=lowbit(i)) (ans+=tr[i])%=mod;
	return ans;
}

int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		pre[i]=last[a[i]];
		last[a[i]]=i;
	}
	LL ans=0;
	for(int i=1;i<=n;i++) {
		LL now;
		if(pre[i]) {
			now=(sum(i)-sum(pre[i]-1))%mod; now+=mod; now%=mod;
			add(pre[i],-(sum(pre[i])-sum(pre[i]-1)));
		} else now=(sum(i)+1)%mod;
		add(i,now);
	}
	printf("%lld\n",sum(n));


	return 0;
}

E − S n a c k E-Snack ESnack 网 络 流 转 最 小 割 + 贪 心 + 推 公 式 网络流转最小割 + 贪心 + 推公式 ++

考虑经典网络流建图:

我们规定 S S S为源点, T T T为汇点, X X X为左部点, Y Y Y为右部点。

( 1 ) S − > X (1)S->X (1)S>X,容量为 a i a_i ai

( 2 ) X − > Y (2)X->Y (2)X>Y,对于左部的每个点与右部的每个点都连边,设右部的某个点为 j j j,容量为 b j b_j bj

( 3 ) Y − > T (3)Y->T (3)Y>T,容量为 c j c_j cj

建图是 n 2 n^2 n2的,而且也没有什么优化的地步,由于这个图的性质比较特殊,我们不妨转换成最小割来求,也就是割掉边权总和最少的边使其不连通。

先考虑第一类边,假设我们割掉 x x x条,由于我们割那条边与我们右边割的代价是互相独立的,所以我们只需要关注切掉了多少条边,也就是可以贪心的切掉 a i a_i ai最小的 x x x条边。

对于第二类边和第三类边,他们剩下了 n − x n-x nx条与 Y Y Y相连的第二类边以及 m m m条与 T T T相连的第三类边,想要不连通只能割掉第二类和第三类其中的一类边,也就是 m i n ( ( n − i ) ∗ b i , c i ) min((n-i)*b_i,c_i) min((ni)bi,ci)。我们按照 c i b i \frac{c_i}{b_i} bici排序,当 n − i n-i ni增大的时候,一定是一部分取 c i c_i ci,一部分取 ( n − i ) ∗ b i (n-i)*b_i (ni)bi,用一个指针维护一下即可,或者可以偷懒写个二分。

int n,m;
LL a[N],prec[N],preb[N];

struct Node {
	LL b,c;
	bool operator < (const Node &W) const {
		return c*W.b<W.c*b;
	}
}node[N];

int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);

	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for(int i=1;i<=m;i++) scanf("%lld%",&node[i].b);
	for(int i=1;i<=m;i++) scanf("%lld%",&node[i].c);
	
	sort(node+1,node+1+m);
	sort(a+1,a+1+n); 
	
	for(int i=1;i<=m;i++) {
		prec[i]=prec[i-1]+node[i].c;
		preb[i]=preb[i-1]+node[i].b;
	}
	LL ans=inf,pre=0,suma=0;
	for(int i=1;i<=n;i++) suma+=a[i];
	
	for(int i=0,j=0;i<=n;i++) {		
		pre+=a[i];
		int l=0,r=m,anss;
		while(l<=r) {
			int mid=(l+r)>>1;
			if(node[mid].c<=node[mid].b*(n-i)) anss=mid,l=mid+1;
			else r=mid-1;
		}
		LL now=prec[anss]+(preb[m]-preb[anss])*(n-i)+pre;
		ans=min(ans,now);
	}
	cout<<ans<<endl;


	return 0;
}

F − T r e e D e g r e e S u b s e t S u m F-Tree Degree Subset Sum FTreeDegreeSubsetSum 貌似是个性质题 待补

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值