ARC 179 & Codeforces Round 955 (Div. 2, with prizes from NEAR!)

ARC

submissions

A

这道题我还不会证明哎。

可以猜想,序列要么是正着排要么是倒着排的。如果都不可以的就输出 No。具体来说,k>0 正着,k<0 倒着。

证明,待补。

B

看到 m10 就想到一定是 bitmask dp 啦!设 dpi,msk 为目前填了 i,下一步可以填 msk 里面的数的方案数。枚举填什么就可以,msk 的维护就是如果在中间出现了自己对应的那个数就把这一位变成 1

时间复杂度 O(n2mm)

C

先说一个错误的做法。

很容易想到,第一次一定要先排一个序,否则比较难搞。排完序我们首尾配对一直到中间,配成 len2 个数(原先 len 个数),再排序,重复这个过程。

为什么是错的呢?考虑一个例子:n=4,r=10,a={10,5,6,9}。这样 |5+6|>r=10,所以不行。

但是我们发现如果只是首尾配对,不配对其他的,一定是可行的。因为:如果都是正数,很显然;如果一个负一个正,加起来一定不超过绝对值的最大值。我们配对以后直接二分插入的位置就可以了。

关于排序有一个 stl 的技巧:stable_sort(id+1,id+1+n,cmp);,其中 cmp 函数中询问。

CF

A

显然,x1,y1x2,y2 的大小关系不能变才能是 Yes

B

我写的是一个玄学复杂度的!就是,我们可以在 xy 的时候已知暴力(其实加的操作可以直接通过余数算出),等到到了 x<y,除了第一次以外,后面就是循环,可以容易求出。

时间复杂度可以感性理解为 O(logyx)

Code
#include <bits/stdc++.h>

using namespace std;

using ll = long long;

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

	int t;
	cin>>t;
	while (t--){
		ll x,y,k,f=0;
		cin>>x>>y>>k;
		while (k>0 && x>=y){
			ll m=x/y;
			m++;
			m*=y;
			ll t=m-x;
			if (t>k){
				cout<<x+k<<"\n";
				f=1;
				break;
			}
			else{
				x+=t;
				k-=t;
				while (x%y==0){
					x/=y;
				} 
			}
		}
		if (f){
			continue;
		}
		if (k==0){
			cout<<x<<"\n";
			continue;
		}
		ll t=y-x;
		if (t>k){
			cout<<x+k<<"\n";
		}
		else{
			k-=t,x=1;
			ll m=y-1;
			ll r=k%m;
			cout<<x+r<<"\n";
		}
	}
	return 0;
}

C

很显然的 dp。设 dpi 为考虑到 i 的最大答案,其中一个转移是 dpi=dpi1。如果用,我们只需要求出可以转移的左右区间即可,这个用二分。区间是单调往右走的,用一个 two pointers 和 set 维护即可。

Code
#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 1e5+5;

ll n,L,R,a[N],s[N],dp[N];

ll fl(int p){
	int l=-1,r=p;
	while (l+1<r){
		int mid=l+r>>1;
		if (s[p]-s[mid]<=R){
			r=mid;
		}
		else{
			l=mid;
		}
	}
	return r;
}

ll fr(int p){
	int l=-1,r=p;
	while (l+1<r){
		int mid=l+r>>1;
		if (s[p]-s[mid]>=L){
			l=mid;
		}
		else{
			r=mid;
		}
	}
	return l;
}

void solve(){
	cin>>n>>L>>R;
	for (int i=1; i<=n; i++){
		dp[i]=0;
		cin>>a[i];
		s[i]=s[i-1]+a[i];
	}
	int pl=-1,pr=-1;
	dp[0]=0;
	multiset<ll> st; 
	for (int i=1; i<=n; i++){
		int lb=fl(i),rb=fr(i);
		if (lb<=rb){
			while (pr<rb){
				pr++;
				st.insert(-dp[pr]);
			}
			while (pl+1<lb){
				pl++;
				st.erase(st.find(-dp[pl]));
			}
		}
		if (st.size()){
			dp[i]=-(*st.begin())+1;
		}
		dp[i]=max(dp[i],dp[i-1]);
	}
	cout<<dp[n]<<"\n";
}

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

	int t;
	cin>>t;
	while (t--){
		solve();
	}
	return 0;
}

D

我们可以用二维前缀和求出每一个 k×k 的矩形我们令 c=1 的贡献是什么。这样问题就变为有若干个数,可以选无数次,能不能和等于另一个数。这个用裴蜀定理即可,具体的,能组成的数一定是那若干个数的 gcd

时间复杂度 O(n2log)

Code
#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 5e2+2;

ll n,m,k,a[N][N],c[N][N];
string s[N];

ll cal(int x1,int y1,int x2,int y2){
	return c[x2][y2]-c[x1-1][y2]-c[x2][y1-1]+c[x1-1][y1-1];
}

ll cal1(int x1,int y1,int x2,int y2){
	return a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];
}

void solve(){
	cin>>n>>m>>k;
	for (int i=1; i<=n; i++){
		for (int j=1; j<=m; j++){
			cin>>a[i][j];
		}
	}
	for (int i=1; i<=n; i++){
		cin>>s[i];
		s[i]=" "+s[i];
		for (int j=1; j<=m; j++){
			if (s[i][j]=='0'){
				c[i][j]=-1;
				a[i][j]*=-1;
			}
			else{
				c[i][j]=1;
			}
		}
	}
	for (int i=1; i<=n; i++){
		for (int j=1; j<=m; j++){
			c[i][j]+=c[i-1][j]+c[i][j-1]-c[i-1][j-1];
			a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
		}
	}
	ll g=0;
	for (int i=1; i+k-1<=n; i++){
		for (int j=1; j+k-1<=m; j++){
			ll sum=cal(i,j,i+k-1,j+k-1);
			sum=abs(sum);
			g=__gcd(g,sum);
		}
	}
	ll sum=cal1(1,1,n,m);
	if ((g==0 && sum==0) || (g!=0 && sum%g==0)){
		cout<<"YES\n";
	}
	else{
		cout<<"NO\n";
	}
}

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

	int t;
	cin>>t;
	while (t--){
		solve();
	}
	return 0;
}

E

有妙妙解法。

sol(n,k) 为答案。(注意,是 0n1 的!)那么,我们 sol(n,k) 可以通过 sol(mx,k)sol(nmx,k1) 算出来,其中 mx 是小于 n 的最大 2 次幂。(很好理解,大于等于 mx 的就会有一位固然是 1,就会是 k1。)设 mx=2c

问题是怎么算。除了内部的,还有一个端点在左边一个端点在右边的。官方题解是维护了三个值,很复杂。有没有直接算的方法?

我们发现,如果 c>k,那么左边最后一个就会有大于 k1,没有贡献,所以必须 ck。这个时候左边所有的都满足,只需要计算右边的。右边的到了 2k1 也不行,因为这样也超过了(当然要和长度取最小值)。所以我们算出了右边的贡献 s=min(2k1,n2c)。那么,答案就要多加上 s2c

直接记忆化搜索即可,复杂度是 O(klogn) 的。代码非常短。

Code
#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const ll mod = 1e9+7;

map<pair<ll,ll>,ll> mp;

ll sol(ll n,ll k){
	if (k==0 || n==1){
		return 1;
	}
	if (mp.count({n,k})){
		return mp[{n,k}];
	}
	ll c=0;
	while ((1ll<<c+1)<n){
		c++;
	}
	ll l=sol(1ll<<c,k),r=sol(n-(1ll<<c),k-1);
	ll ans=l+r;
	if (c<=k){
		ll s=min((1ll<<k)-1,n-(1ll<<c));
		ans+=s%mod*((1ll<<c)%mod)%mod;
	}
	mp[{n,k}]=ans%mod;
	return ans%mod;
}

void solve(){
	ll n,k;
	cin>>n>>k;
	cout<<sol(n,k)<<"\n";
}

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

	int t;
	cin>>t;
	while (t--){
		solve();
	}
	return 0;
}
posted @   SFlyer  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示