11.15 gryz校測(題解分析報告)

T1 心有靈犀 (cooperate)

題目大意

給你一個不超過 \(10^9\) 的數字 \(n\) 和一個交換次數上限 \(k\)node

每次操做對這個 數字 \(n\) 的其中兩位進行交換,ios

好比 201 能夠換成 102,git

讓你進行 \(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;
}

感謝觀看

相關文章
相關標籤/搜索