1. 程式人生 > >HGOI-國慶七連測-day3

HGOI-國慶七連測-day3

題解

講道理,真的很譴責這種掛著羊頭賣著恐龍肉的操作。標題寫著普及訓練難度結果是NOI/CTSC的題orz 真的爆零,我一道題都不會寫orz,出題人是江蘇高考415的dalao…

第一題——高考題(gaokao)

【題目描述】

  • 給出二元組序列[ai,bi][a_i,b_i]
  • 給出方程:Ti={ai+bii=1max(Ti1,j=1iai)+bii1T_i=\begin{cases} a_i+b_i &i=1\\ max(T_{i-1},\sum_{j=1}^{i}{a_i})+b_i &i\ne 1\\ \end{cases}
  • 現在要求重新對二元組序列排序,使得序列T當中的最大的值最小。
  • 這個真的是高考題…orz,那我高考的時候怕是要gg了…

  • 其實你可以手動推一下:

    T1=a1+b1T_1=a_1+b_1

    T2=max(T1,a1+a2)+b2=max(a1+b1+b2,a1+a2+b2)T_2=max(T_1,a_1+a_2)+b_2=max(a_1+b_1+b_2,a_1+a_2+b_2)

    2)

    T3=max(T2,a1+a2+a3)+b3=max(a1+b1+b2+b3,a1+a2+b2+b3,a1+b1+b2+b3)T_3=max(T_2,a_1+a_2+a_3)+b_3=max(a_1+b_1+b_2+b_3,a_1+a_2+b_2+b_3,a_1+b_1+b_2+b_3)

  • 總結規律你會發現其實Ti=max(k=1jai+k=jibi)T_i=max(\sum_{k=1}^{j}a_i+\sum_{k=j}^{i}b_i)

  • 然後你會發現這個並沒有什麼用…

  • 只能告訴你按照min(ai,bj)<min(aj,bi)min(a_i,b_j)<min(a_j,b_i)來排…

  • 但這個還是錯解…但資料是錯誤解造出來的orz

  • 相似題目(其實是一樣的)可以看這個洛谷2123,lzj大佬寫的是正解…

#include<iostream> 
#include<cstdio>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
inline void fff(){
	freopen("gaokao.in","r",stdin);
	freopen("gaokao.out","w",stdout);
}
const int N=100010;
inline int read(){
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x;
}
int n;
struct node{
	LL t1,t2;
	LL d;
	bool operator < (const node x)const{
//		if(d!=x.d) return d<x.d;
//		if(d<=0) return t1<x.t1;
//		return t2>x.t2;
		return min(t1,x.t2)<min(t2,x.t1);
	}
}a[N];
LL c[N];
int main(){
//	fff();
	int T;
	cin>>T;
	while(T--){
		memset(a,0,sizeof(a));
		memset(c,0,sizeof(c));
		n=read();
		for(int i=1;i<=n;i++){
			a[i].t1=(LL)read();
			a[i].t2=(LL)read();
			if(a[i].t1>a[i].t2) a[i].d=1;
			else if(a[i].t1<a[i].t2) a[i].d=-1;
			else a[i].d=0;
		}
		sort(a+1,a+n+1);
		LL sum=0;
		for(int i=1;i<=n;i++){
			sum+=a[i].t1;
			c[i]=max(c[i-1],sum)+a[i].t2;
		}
		printf("%lld\n",c[n]);
	}

}

第二題——高數題(gaoshu)

【題目描述】

  • 在點權樹中,邊上存在顏色,規定所走路徑不能前後經過相鄰的顏色,求樹上合法路徑權值和。
  • 唯一一個還能夠想想做法的題。和上一道和下一道的玄之又玄的做法完全不一樣。
  • 三十分做法:暴力列舉開始節點ii,然後對於每一個節點O(n)O(n)模擬,最終結果由於出發節點和終點是等價的,所以結果除以2。最終複雜度O(n2)O(n^2)
  • 滿分做法:
    • 假設根節點root,則從根節點出發向下進行路徑操作。對於每一顆子樹來說,他子樹下的節點的權值經過和是固定的。那麼維護下從dang下面節點延伸到當前節點的權值和與he合法途徑的條數。
    • 顯然:
      • 當前節點為根節點的所有合法路徑的權值和 = 之前深搜的所有子節點向上返回的邊數之和*當前子節點返回的分數+ 之前深搜的所有子節點向上返回的分數之和 *當前子節點返回的邊數+之前深搜的所有子節點向上返回的邊數之和*當前子節點返回的邊數* 當前點的權。
    • 由於要考慮顏色,則需要將當前子樹的相同路徑的子樹進行“合併操作”。
    • 然後就可以求出解了orz。
#include<iostream> 
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<map> 
using namespace std;
typedef long long LL;
const int N=300010;
inline void fff(){
	freopen("gaoshu.in","r",stdin);
	freopen("gaoshu.out","w",stdout);
}
inline int read(){
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x;
}
int n;
int a[N],d[N];
struct Edge{
	int from,to,color;
};
vector<Edge> edge;
vector<int> G[N];
map<int,pair<LL,LL>> tt[N];
bool visited[N];
bool cmp1(int i,int j){
	return edge[i].color<edge[j].color;
}
LL num[N],val[N];
LL ans;
void dfs(int u,int fuck){
	int siz=G[u].size();
	visited[u]=true;
	sort(G[u].begin(),G[u].end(),cmp1);
	for(int i=0;i<siz;i++){
		Edge &e=edge[G[u][i]];
		if(visited[e.to]) continue;
		dfs(e.to,e.color);
		ans+=(num[u]-tt[u][e.color].first)*val[e.to]+(val[u]-tt[u][e.color].second)*num[e.to]+(num[u]-tt[u][e.color].first)*num[e.to]*a[u];
		tt[u][e.color].first+=num[e.to];
		tt[u][e.color].second+=val[e.to];
		num[u]+=num[e.to];
		val[u]+=val[e.to];
		ans+