题解
讲道理,真的很谴责这种挂着羊头卖着恐龙肉的操作。标题写着普及训练难度结果是NOI/CTSC的题orz
真的爆零,我一道题都不会写orz,出题人是江苏高考415的dalao…
第一题——高考题(gaokao)
【题目描述】
- 给出二元组序列 。
- 给出方程:
- 现在要求重新对二元组序列排序,使得序列T当中的最大的值最小。
-
这个真的是高考题…orz,那我高考的时候怕是要gg了…
-
其实你可以手动推一下:
-
总结规律你会发现其实
-
然后你会发现这个并没有什么用…
-
只能告诉你按照 来排…
-
但这个还是错解…但数据是错误解造出来的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)
【题目描述】
- 在点权树中,边上存在颜色,规定所走路径不能前后经过相邻的颜色,求树上合法路径权值和。
- 唯一一个还能够想想做法的题。和上一道和下一道的玄之又玄的做法完全不一样。
- 三十分做法:暴力枚举开始节点 ,然后对于每一个节点 模拟,最终结果由于出发节点和终点是等价的,所以结果除以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+=val[e.to]+num[e.to]*a[u];
}
num[u]-=tt[u][fuck].first;
val[u]-=tt[u][fuck].second;
num[u]++;
val[u]+=a[u]*num[u];
}
int main(){
// fff();
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<n;i++){
int u,v,w;
u=read(),v=read(),w=read();
edge.push_back((Edge){u,v,w});
G[u].push_back(edge.size()-1);
edge.push_back((Edge){v,u,w});
G[v].push_back(edge.size()-1);
}
ans=0;
dfs(1,-1);
cout<<ans;
}
第三题——高中题(gaozhong)
【题目描述】
- 给出n个节点,m条带权边,前n-1条是好边,要求调整边的权值使得图的最小生成树只由好边构成。求调整权值代价和最小是多少(是指每对边权修改1代价就是1)
- 很明显,这个题我不会。
- 可以看得出,如果要让好边都是最小生成树当中的边,根据kruskal算法,好边的权值只能够减,坏边的权值只能够加。那么令任意一组好、坏边 ,修改前的权值是 ,调整值是 ,那么可以得出 ,再往后推一步,就可以得到 ,而我们的目的就是求出满足这些不等式组的非负最小解(最小是指和最小)。
- 那我们就可以将他们转化成二分图的模型,好边属于同一个集合,坏边属于一个集合,要求好边与坏边之间的匹配值满足不等式。
- 然后就是KM求最大完美匹配了orz。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=550;
inline void fff(){
freopen("gaozhong.in","r",stdin);
freopen("gaozhong.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;
}
const int INF=10000000;
int n,m,nx,ny;
int s[N],e[N],d[N],belong[N],ss[N];
int f[N][N],z[N][N],lx[N],ly[N];
bool visx[N],visy[N];
bool dfs(int u,int pre,int id){
if(u==e[id]) return true;
for(int i=1;i<=n;i++){
if(i!=pre&&f[u][i]&&dfs(i,u,id)){
z[f[u][i]][id]=d[f[u][i]]-d[id];
return true;
}
}
return false;
}
bool dfs(int x){
visx[x]=true;
for(int i=1;i<=ny;i++){
if(!visy[i]){
int relax=lx[x]+ly[i]-z[x][i];
if(!relax){
visy[i]=true;
if(!belong[i]||dfs(belong[i])){
belong[i]=x;
return true;
}
}else ss[i]=min(ss[i],relax);
}
}
return false;
}
void KM(){
for(int i=1;i<=nx;i++) lx[i]=-INF;
for(int i=1;i<=nx;i++)
for(int j=1;j<=ny;j++)
lx[i]=max(lx[i],z[i][j]);
for(int i=1;i<=nx;i++){
memset(ss,0x3f,sizeof(ss));
while(true){
memset(visx,false,sizeof(visx));
memset(visy,false,sizeof(visy));
if(dfs(i)) break;
int delta=INF;
for(int j=1;j<=ny;j++)
if(!visy[j]) delta=min(delta,ss[j]);
if(delta==INF) return;
for(int j=1;j<=nx;j++)
if(visx[j]) lx[j]-=delta;
for(int j=1;j<=ny;j++)
if(visy[j]) ly[j]+=delta;
else ss[j]-=delta;
}
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
s[i]=read();
e[i]=read();
d[i]=read();
}
memset(f,0,sizeof(f));
memset(z,0,sizeof(z));
for(int i=1;i<n;i++) f[s[i]][e[i]]=f[e[i]][s[i]]=i;
for(int i=n;i<=m;i++) dfs(s[i],0,i);
nx=ny=m;
KM();
int ans=0;
for(int i=1;i<n;i++)ans+=lx[i];
for(int i=n;i<=m;i++)ans+=ly[i];
printf("%d",ans);
}