Loading

题解-CF1485

哇,我回到家了!太好了!终于可以像样地学习了!

写篇比赛题解恢复一下状态。


A. Add and Divide

如果 \(b=1\),那么必须先把 \(b\)\(1\)

否则答案上界是 \(\log_ba\),枚举 \(b\) 加的次数即可。

int mylog(int a,int b){
	int res=0;
	while(b) b/=a,++res;
	return res;
}
 
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
 
	int cas;
	for(cin>>cas;cas--;){
		int a,b;
		cin>>a>>b;
		int res=0;
		if(b==1) b=2,++res;
		int x=mylog(b,a),y=x;
		R(t,x)if(t){
			y=min(y,mylog(b+t,a)+t);
		}
		
		cout<<res+y<<'\n';
	}
 
	return 0;
}

B. Replace and Keep Sorted

有点细节 /ch

对于区间 \([l,r]\),改变其中一个点,满足题目条件。

显然只有 \(l,r\) 两个点是不可以预处理的,所以前缀和 \(+\) 端点特殊处理即可。

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
 
	int n,q,k;
	cin>>n>>q>>k;
	vi a(n);
	vector<i64> pre(n+1);
	R(i,n) cin>>a[i],--a[i];
	R(i,n) pre[i+1]=pre[i]
		+(i==n-1?k:a[i+1])
		-(i==0?0:a[i-1]+1)-1;
	while(q--){
		int l,r;
		cin>>l>>r,--l;
		if(r-l==1){
			cout<<k-1<<'\n';
		} else {
			i64 res=pre[r-1]-pre[l+1];
//			cout<<res<<'\n';
			res+=a[l+1]-0-1;
			res+=k-a[r-2]-1-1;
			cout<<res<<'\n';
		}
	}
 
	return 0;
}

C. Floor and Mod

写了个乱七八糟的做法不知道怎么过的,写个良心做法。

\(c=a\bmod b=\lfloor\frac ab\rfloor\)

由于 \(c<b\)\(cb\le a\le x\),所以 \(c^2\le x\),可以枚举这个 \(c\)

对于每个 \(c\),需要统计有多少个 \(c<b\le y\) 满足 \(1\le c(b+1)\le x\)

这个东西就是 \(\max(0,\min(y,\lfloor\frac{x}{c}\rfloor-1)-c)\)

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
 
	int cas;
	for(cin>>cas;cas--;){
		int x,y;
		cin>>x>>y;
		i64 res=0;
		for(int c=1;c*c<=x;++c){
			res+=max(0,
			min(y,x/c-1)-c);
		}
		
		cout<<res<<'\n';
	}
 
	return 0;
}

D. Multiples and Power Differences

关键点:\(1\le a\le 16\)\(a^4\le 65536\)

考虑如果 \(b\) 可以为 \(0\),那么直接黑白间隔染色,然后白格子填 \(0\),黑的填 \(a^4\)

否则有个神奇的结论:

\[{\rm lcm_{x=1}^{16}} x=720720 \]

大家都加个 \(720720\) 即可。

// 下次别写 mygcd,写 gcd 好啊
int mygcd(int a,int b){
	return a?mygcd(b%a,a):b;
}
 
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
 
	int n,m;
	cin>>n>>m;
	vector<vi> a(n,vi(m));
	R(i,n)R(j,m) cin>>a[i][j];
	R(i,n)R(j,m){
		int x=720720;
		if((i+j)&1) x+=a[i][j]
		*a[i][j]*a[i][j]*a[i][j];
		cout<<x<<" \n"[j==m-1];
	}
 
	return 0;
}

E. Move and Swap

这个 EE 点点毒瘤,建议先做 F

一步一步转化问题:

  1. 转化为不准交换,没层两个棋子可以不按边走一次。

  2. 转化为在每个边层中选一条边。如果上下两层的边选出来没有共同点(type 0),那么这一层两个棋子的位置就是这两个点;否则(type 1),这一层还有一个棋子可以是任意位置。对于最后一层有一个点可以随意选。

\(f[u]\) 表示选到 \(u\) 和父亲的边这一层,且下端的点是 \(u\) 的最优方案。

对于 type 0 情况可以离散化然后前后缀最大值维护 dp

对于 type 1 情况可以对于每个点单独转移,这个任意点必然是同层内最小或最大的点。

细节看代码吧。

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

	int cas;
	for(cin>>cas;cas--;){
		int n;
		cin>>n;
		vector<vi> adj(n);
		R(u,n)if(u){
			int v;
			cin>>v,--v;
			adj[u].push_back(v);
			adj[v].push_back(u);
		}
		
		vi a(n);
		R(u,n)if(u) cin>>a[u];
		
		vi fa(n,-1),d(n);
		vi mx(n,-inf32),mn(n,inf32);
		vector<vi> at(n);
		function<void(int)> dfs=[&](int u){
			mn[d[u]]=min(mn[d[u]],a[u]);
			mx[d[u]]=max(mx[d[u]],a[u]);
			at[d[u]].push_back(u);
			for(const int &v:adj[u]){
				if(v==fa[u]) continue;
				fa[v]=u,d[v]=d[u]+1;
				dfs(v);
			}
		};
		dfs(0);
		
		int maxd=*max_element(all(d));
		vector<i64> f(n,-inf64);
		f[0]=0;
		R(i,maxd){
			vi ds;
			for(const int &u:at[i])
				ds.push_back(a[u]);
			sort(all(ds));
			ds.erase(unique(all(ds)),ds.end());
			int sn=sz(ds);
			
			vector<i64> pg(sn+1,-inf64),sg(sn+1,-inf64);
			for(const int &u:at[i]){
				int x=lower_bound(all
				(ds),a[u])-ds.begin();
				pg[x+1]=max(pg[x+1],f[u]-a[u]);
				sg[x]=max(sg[x],f[u]+a[u]);
			}
			R(i,sn+1)if(i) pg[i]=max(pg[i],pg[i-1]);
			L(i,sn) sg[i]=max(sg[i],sg[i+1]);
			
			for(const int &u:at[i+1]){
				int x=lower_bound(all(ds),
				a[fa[u]])-ds.begin();
				f[u]=max(pg[x]+a[fa[u]],sg[x]-a[fa[u]]);
				f[u]=max(f[u],f[fa[u]]+
				max(mx[i]-a[fa[u]],a[fa[u]]-mn[i]));
			}
		}
		
		i64 res=0;
		for(const int &u:at[maxd])
			res=max(res,f[u]+max(
			mx[maxd]-a[u],a[u]-mn[maxd]));
		cout<<res<<'\n';		
	}

	return 0;
}

F. Copy or Prefix Sum

剖析一下题目:如果不去重,答案应该是 \(2^n\)

\(s_i=\sum_{i=1}^n b_i\)

设选择 \(b_i=a_i\)type 0,否则是 type 1

正解是个非常 naivedp,设 \(f[i][j]\) 表示决定到了 \(b_i\),上一个 type 1 的位置是 \(j\)

\[\begin{cases} f[i+1][j]\leftarrow f[i][j]\\ f[i+1][i]\leftarrow \sum_j f[i][j]\cdot [b_i-(b_j+s_{i-1}-s_j)\neq b_i]\\ \end{cases}\]

所以可以把 dp 前一维压掉,剩下一维如下修改:

\(g[x]\) 表示当前 \(s_j-b_j=x\)\(f[i][j]\) 之和。

再维护一个总的 dp 的和,用 std::map 维护 dp,时间复杂度 \(\Theta(n\log n)\)

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
 
	int cas;
	for(cin>>cas;cas--;){
		int n;
		cin>>n,++n;
		vector<i64> a(n),s(n);
		R(i,n)if(i){
			cin>>a[i];
			s[i]=s[i-1]+a[i];
		}
		
		map<i64,int> g;
		g[0]=1;
		int sum=1;
		R(i,n)if(i){
			int F=(mint(sum)-g[s[i-1]]).x;
			g[s[i]-a[i]]=(mint(g[s[i]-a[i]])+F).x;
			sum=(mint(sum)+F).x;
		}
		
		cout<<sum<<'\n';
	}
 
	return 0;
}

我好菜,好菜,好菜。

posted @ 2021-02-15 21:45  George1123  阅读(141)  评论(3编辑  收藏  举报