11.15 gryz校测(题解分析报告)

T1 心有灵犀 (cooperate)

题目大意

给你一个不超过 \(10^9\) 的数字 \(n\) 和一个交换次数上限 \(k\)

每次操作对这个 数字 \(n\) 的其中两位进行交换,

比如 201 可以换成 102,

让你进行 \(k\) 次操作,求出交换后最大的数字和最小的数字的差的绝对值

思路

  • 某一位的数字可以和它本身进行交换
  • 交换的数字不可以有前导零(即第一位不可以是 \(0\)

解法

  • 数据不超过 \(10^9\) ,可以考虑将每一位进行拆分
  • 还记得我们学深搜时的全排列吗?
  • 暴力枚举在 \(k\) 次交换下的广义全排列,挨个比较得到 \(max \ min\) ,相见即可

误导

  • 这道题的关键是大家很容易误以为是贪心,而一般贪心是错的
  • 举例:
    • \(k=2\) 时的 \(970979\),贪心求出最大值是 \(999077\)
    • 但实际上可以达到的最大值是 \(999770\)
    • 所以这题不是个简单的贪心。

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <string.h>
using namespace std;
#define int long long

const int manx=1e6+10;
const int mamx = 1e6 + 11;
const int  mod = 2123400401301379571;
const int inf = 0x3f3f3f3f;

inline int read() {
  char c = getchar(); int x = 0, f = 1;
  for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
  for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
  return x * f;
}
int t,n,m,now[manx],cnt,maxn,minx,k; 
int a[manx],b[manx],js,bz;
 void dfs_min(int k,int cnt){
	if(k <= 0 || cnt >= js){
		int s = 0;
		for(int i = 1;i <= js;i++)
			s = s*10 + b[i];
		if(s > bz) minx = min(s,minx);//s-->minx,100 --> 0
		/*
			bz : 标准
			含义是当前这个序列组成的数必须比 10^(js-1)大,(防止前导零)
		*/
        return;
	}
	for(int i = cnt + 1;i <= js; i++){
		if(b[i] <= b[cnt]){
			swap(b[i],b[cnt]);
			dfs_min(k-1,cnt+1);
			swap(b[i],b[cnt]);//回溯
		}
	}
	dfs_min(k,cnt+1);//当前这个数即为最小数,直接搜索下一位
}
 void dfs_max(int k,int cnt){
	if(k <= 0 || cnt >= js){
		int s = 0;
		for(int i = 1;i <= js;i++)
			s = s*10 + a[i];
		maxn = max(maxn,s);
		return;
	}
	for(int i = cnt + 1;i <= js; i++){
		if(a[i] >= a[cnt]){
			swap(a[i],a[cnt]);
			dfs_max(k-1,cnt+1);
			swap(a[i],a[cnt]);
		}
	}
	dfs_max(k,cnt+1);//原理同上
}
 void solve(){
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		memset(now,0,sizeof(now));
		cnt = js = 0;
		minx = inf, maxn = 0;
		n = read();k = read();
		while(n){
			now[++cnt] = n%10; //倒序去位数
			n = n/10;
		}
		bz = 1;
		for(int i = cnt;i >= 1; i--){
			a[++js] = now[i],b[js] = now[i];//不可以连等吗 
			bz *= 10;
		}
		bz = bz/10;//位数是(js-1)
		dfs_max(k,1),dfs_min(k,1);
		cout<< maxn - minx << '\n';
}
 signed main(){
	t = read();
	while(t--) solve();
	return 0;
}

T2 不服来战 (challenge.cpp)

题面

  • 你有一列 \(N\) 盏灯,初始时有些是开的,有些是关的, 每盏灯有各自的权值。
  • 每次操作你可以改变任意连续 \(K\) 盏灯的开关状态。
  • 你可以操作任意多次,求最终最大的亮着的灯的权值和

解法

优化

  • 再稍作分析,不难发现,如果一组内“初始时是关的”的灯为偶数个,那么我们可以 做到只把它们全部打开,也就是说打开了这一组所有的灯
  • 如果一组内“初始时是关的” 的灯为奇数个,这个时候我们不能把所有的灯都打开,只好作出让步,选择放弃亮度最小的那盏灯。每一组都如此处理,最后加起来。

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;

const int manx=1e6+10;
const int mamx = 1e6 + 11;
const ll mod = 2123400401301379571;
const int inf = 0x3f3f3f3f;

inline int read() {
  char c = getchar(); int x = 0, f = 1;
  for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
  for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
  return x * f;
}
int T ,a[manx] ,n ,k ,ans;
bool pd[manx]; 
inline int Check(int n ,int k ,bool pd[] ,int a[]){
	int ret = 0;
	for(int i = 1;i <= k;i ++){
		int cnt = 0,minx = inf;
		for(int j = i; j <= n; j+=k){
			ret += a[j];
			minx = min(minx ,a[j]);
			if(pd[j] == 0) cnt++;//记录零的个数
		}
		if(cnt % 2 != 0) ret -= minx; 
	}
	return ret;
}
int main(){
	T = read();
	while(T--){
		ans = 0;
		n = read(); k = read();
		for(int i = 1;i <= n; i++) pd[i] = read();
		for(int i = 1;i <= n; i++) a[i] = read();
		if(k == 1){
			for(int i =1;i <= n; i++) ans += a[i];
		}else{
			ans = Check(n ,k ,pd ,a);
            /*
            	两种情况,第一个是不翻转1-k,得到的最小值,
            	        第二个是翻转后得到的最小值,
            	        (因为就两种情况前 1-k 翻与不翻)
            */
			for(int i = 1;i <= k; i++) pd[i] ^= 1;//pd [i] ---> pd [k]  100 --> 0 
			ans = max(ans,Check(n ,k ,pd ,a));
		}
		cout<<ans<<'\n'; 
	}
	return 0;
}


T3 铁路网络 (network.cpp)

题面

  • 给你一棵有根树,每条边有边权。
  • 实现两种操作:
  • \(a\). 给某一条路径上所 有边的权值加上一个数;
  • \(b\). 询问某棵子树内所有点对的距离和。

思路

  • 显然可以用树链剖分进行操作,和线段树进行维护,

  • 路径修改,求子树间两点路径的总和(不是子树查询)

  • 值得注意的是,点权边权,操作路径修改是要注意 \(dfn[u]\) 的位置,避免多加,或少加

  • 不妨设点 \(x\) 与它的父亲 \(fa[x]\) 相连的边的权值为 \(p[x]\)

  • 考虑 \(p[x]\) 会对那些点对产生贡献?

  • 显然是经过 \(x——fa[x]\) 这条边的那些点对。记 \(size_[x]\) 为以 \(x\) 为根的子树的大小。

  • 则经过 \(x——fa[x]\) 的点对有 \(size_[x]×(size_[i] – size_[x])\)

  • 于是,子树 \(i\) 内的点 \(x\) 的贡献

  • \[p[x]\times size_[x]\times size_[i] - p[x] \times size_[x]^2 \]

  • \[\begin{alignedat}{3} \sum_{i=1}p[x]\times size_[x]\times size_[i] - p[x] \times size_[x]^2\\ =size_[i]\times \sum_{i=1}(p[i]\times size_[i])-\sum_{i=1}(p[i]\times size_[i]^2)\\ \end{alignedat} \]

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string.h>
#include <queue>
#include <algorithm>
using namespace std;
#define ll long long

const int manx = 1e6;
const int mod = 2123400401301379571;
const int inf = 0x3f3f3f3f;
 
inline int read(){
	char c = getchar(); int x = 0, f = 1;
	for( ;!isdigit(c);c = getchar()) if(c == '-') f = -1;
	for( ;isdigit(c);c = getchar()) x = x*10 + (c^48);
	return x * f;
}
struct node{
	int v,nxt;
}e[manx];
int head[manx],cnt,n,m,dep[manx],fa[manx],size_[manx],val[manx],pre[manx],tp[manx],dfn[manx],hson[manx];
int js,sigma1,sigma2;
inline void add(int u,int v){
	e[++cnt].nxt = head[u];
	e[cnt].v = v;
	head[u] = cnt;
}

namespace Tree{
	#define ls i<<1
	#define rs i<<1|1
	struct tree{
		int l;int r;
		ll sum,lazy;
	}tr[manx<<3];
	inline void up(int i){
		tr[i].sum = tr[ls].sum + tr[rs].sum ; 
	}
	inline void down(int i){
		if(tr[i].lazy){
			ll x = tr[i].lazy ;
			tr[ls].sum += (tr[ls].r - tr[ls].l +1)*x;
			tr[rs].sum += (tr[rs].r - tr[rs].l +1)*x;
			tr[ls].lazy += x;
			tr[rs].lazy += x;
			tr[i].lazy = 0; 
		}
	}
	inline void build(int i,int l,int r){
		tr[i].l = l;tr[i].r = r;
		if(l == r){
			tr[i].sum = val[pre[l]];
			return ; 
		}
		int mid = (l + r)>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
		up(i);
	} 
	inline void add(int i,int l,int r,int w){
		if(tr[i].l >= l && tr[i].r <= r){
			tr[i].sum += (tr[i].r - tr[i].l +1)*w;
			tr[i].lazy += w;
			return; 
		}
		down(i);
		int mid=(tr[i].l +tr[i].r )>>1;
		if(mid >= r)add(ls,l,r,w);
		else if(mid < l)add(rs,l,r,w);
		else add(ls,l,mid,w),add(rs,mid+1,r,w);
		up(i);
	}
	inline ll only_query(int i,int u){
		if(tr[i].l  == tr[i].r ){
			return tr[i].sum ;
		}
		down(i);
		int mid=(tr[i].l +tr[i].r )>>1;
		if(mid >= u) return only_query(ls,u);
		else if(mid < u) return only_query(rs,u);
	}
	inline ll query(int i,int l,int r){
		if(tr[i].l >= l && tr[i].r <= r){
			return tr[i].sum ; 
		}
		down(i);
		int mid=(tr[i].l +tr[i].r )>>1;
		if(mid >= r)return query(ls,l,r);
		else if(mid < l)return query(rs,l,r);
		 return query(ls,l,mid)+query(rs,mid+1,r);
	}
}
namespace Node{
	inline void dfs1(int u,int pre,int d){
		fa[u] = pre; dep[u] = d;size_[u] = 1;
		for(int i = head[u]; i;i = e[i].nxt){
			int v = e[i].v ;
			if(v != pre){
				dfs1(v,u,d+1);
				size_[u] += size_[v];
				if(!hson[u] || size_[hson[u]] < size_[v]){
					hson[u] = v;
				}
			}
		}
	}
	inline void dfs2(int u,int top){
		tp[u] = top;
		dfn[u] = ++js;
		pre[js] = u;
		if(!hson[u])return;
		dfs2(hson[u],top);
		for(int i = head[u];i;i = e[i].nxt ){
			int v = e[i].v ;
			if(v != fa[u] && v != hson[u]){
				dfs2(v,v);
			}
		}
	}
	inline void add(int u,int v,int w){
		while(tp[u] != tp[v]){
			if(dep[tp[u]] < dep[tp[v]])swap(u,v);
			Tree::add(1,dfn[tp[u]],dfn[u],w);
			u = fa[tp[u]];
		}
		if(dep[u]>dep[v])swap(u,v);
		Tree::add(1,dfn[u] + 1,dfn[v],w);
	}
	inline void query(int u){
		for(int i = head[u]; i; i = e[i].nxt ){
			int v = e[i].v ;
			if(v != fa[u]){
				Node::query(v);
				int s = Tree::only_query(1,dfn[v]);
			//	cout<<s<<" "<<v<<endl;
				sigma1 += (s * size_[v]);
				sigma2 += (s * size_[v] * size_[v]);
			}
		}
		return;
	}
}
char a[9];
int  main(){
	//freopen("pp.in","r",stdin);
	//freopen("network.out","w",stdout);
	n = read();m = read();
	for(int i = 1;i < n; i++){
		int x = read(), y = read();
		val[i+1] = y;
		add(i+1,x);
		add(x,i+1);
	}
	Node::dfs1(1,0,1),Node::dfs2(1,1),Tree::build(1,1,n);
	for(int i = 1;i <= m; i++){
		cin>>a;
		ll ans = 0;
		ll fan = 0;
		int x,y,z;
		if(a[0] == 'I'){
			x = read(); y = read(); z = read();
			Node::add(x,y,z);
		}else{
			x = read();
			sigma1 = 0;
			sigma2 = 0;
			Node::query(x);
			cout<<size_[x] * sigma1 - sigma2 <<endl; 			
		}
	}
	return 0;
}

感谢观看

posted @ 2020-11-15 22:25  zxsoul  阅读(152)  评论(0编辑  收藏  举报