T1【TJOI2015旅游】
WOJ 4322
【只能买卖一次哦!!!】
一道树链剖分,不过我还不会,就先跳过。
30
30
30分纯暴力:
计算LCA,分两段算,再算一次两段连在一起的,取MAX
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return f==1?x:-x;
}
const int N=50010;
struct edge{int v,nxt;}e[N*2];
int first[N],cnt=0;
inline void add(int u,int v){e[++cnt].v=v;e[cnt].nxt=first[u];first[u]=cnt;}
int n,m,pri[N],ans;
int fa[N][18],dep[N];
void predfs(int x,int f){
fa[x][0]=f;
for(int i=1;(1<<i)<=dep[x];i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=first[x];i;i=e[i].nxt){
if(e[i].v==f)continue;
dep[e[i].v]=dep[x]+1;
predfs(e[i].v,x);
}
}
int LCA(int a,int b){
if(dep[a]<dep[b])swap(a,b);
int t=dep[a]-dep[b];
for(int i=0;i<=16;i++)
if(t&(1<<i))a=fa[a][i];
if(a==b)return a;
for(int i=16;i>=0;i--)
if(fa[a][i]!=fa[b][i]){
a=fa[a][i];b=fa[b][i];
}
return fa[a][0];
}
void in(){
n=read();
for(int i=1;i<=n;i++)
pri[i]=read();
for(int i=1;i<n;i++){
int u,v;
u=read();v=read();
add(u,v);add(v,u);
}
dep[1]=1;
predfs(1,0);
m=read();
}
int solve_1(int a,int b,int v){
int r=2e9;
while(1){
if(r<pri[a])ans=max(ans,pri[a]-r);
if(r>pri[a])r=pri[a];
if(a==b)break;
pri[a]+=v;
a=fa[a][0];
}
return r;//最小数
}
int solve_2(int a,int b,int v){
int r=0;
while(1){
if(r>pri[a])ans=max(ans,r-pri[a]);
if(r<pri[a])r=pri[a];
if(a==b)break;
pri[a]+=v;
a=fa[a][0];
}
return r;//最大数
}
void work_1(){
int a,b,k,lca,e,f;
while(m--){
a=read();b=read();k=read();
lca=LCA(a,b);
ans=0;
e=solve_1(a,lca,k);
f=solve_2(b,lca,k);
pri[lca]+=k;
printf("%d\n",max(ans,f-e));
}
}
int main(){
in();
work_1();//只能买卖一次的暴力
return 0;
}
T2【迅雷】
WOJ 2876
唯一 一道考场做出的题,确实有点水,看完题面,就大概知道是类似于
k
r
u
s
k
a
l
kruskal
kruskal的并查集做法。
输入边,排序,从大到小看。
如果电脑和资源器连通,就结束并输出当前这条边的长度。
【注意每次合并都要把节点信息更新至祖先节点!!!还有别忘了正无穷"+00"哦】
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return f==1?x:-x;
}
const int N=100010;
int n,m;
struct edge{int u,v,w;}e[N];
inline int SORT(const edge &x,const edge &y){return x.w>y.w;}
int id[N],fa[N];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int main(){
n=read();m=read();
int p,y;
y=read();
for(int i=1,u;i<=y;i++){
u=read();
id[u]=1;
}
for(int i=1;i<=m;i++){
e[i].u=read();e[i].v=read();e[i].w=read();
}
p=read();
int flag=0;
for(int i=1,u;i<=p;i++){
u=read();
if(id[u]==1)flag=1;
id[u]=2;
}
if(flag){
printf("+00");
return 0;
}
sort(e+1,e+m+1,SORT);
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++){
int fu=find(e[i].u),fv=find(e[i].v);
if(fu==fv)continue;
if(id[fu]+id[fv]==3){
printf("%d",e[i].w);
break;
}
fa[fv]=fu;
if(id[fu]!=id[fv])id[fu]=max(id[fu],id[fv]);
}
return 0;
}
T3【Alice&Bob】
WOJ 4321
刚开始想的,用
a
l
i
c
e
alice
alice直接推
b
o
b
bob
bob,但失败了,不知有没有这个关系。
先贴一份
10
10
10分代码,若大佬们有什么想法,发现,可以交流交流。
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}return f==1?x:-x;
}
const int N=100010;
int n,a[N],b,smax[N*4],maxx=0;long long ans=0;
void add(int x,int v){for(;x<=n;x+=x&-x)smax[x]=max(smax[x],v);}
int query(int x){int ret=0;for(;x;x-=x&-x)ret=max(ret,smax[x]);return ret;}
int main(){
n=read();for(int i=1;i<=n;i++)a[i]=read();
for(int i=n;i>0;i--){
if(a[i]==a[i+1])b=maxx+1;else{b=query(a[i])+1;add(a[i],b);}
maxx=max(maxx,b);ans+=b;
}
printf("%lld",ans);
return 0;
}
正解 贪心+拓扑排序
基本思想是先求出每一个的高度,再算下降子序列。
加了特技的拓扑排序:用
s
e
t
set
set,内部自动从小到大排序。
连边(大->小)建图后肯定是一个DAG,就会想拓扑排序。
要令
b
o
b
bob
bob序列之和尽量大,就是大的尽量向前,小的尽量向后。
虽然做不来,但是看题解的时候还是很容易,就详见代码吧。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
struct edge{int v,nxt;}e[N<<1];
int first[N],cnt=0;
int n,a[N],in[N],hei[N],last[N];
inline void add(int u,int v){e[++cnt].v=v;e[cnt].nxt=first[u];first[u]=cnt;in[v]++;}
set<int>s;//有序的 从小到大
void topsort(){
for(int i=1;i<=n;i++)
if(!in[i])s.insert(i);//节点编号从小到大,前面的可以贪心比后面高些,这样下降子序列更长
int t=0;
while(!s.empty()){
int u=*s.begin();s.erase(s.begin());//加入,重新排序,一直弹出最小的
//没限制,就可以弹最小的
hei[u]=n-(t++);
for(int i=first[u];i;i=e[i].nxt){
in[e[i].v]--;
if(!in[e[i].v])s.insert(e[i].v);
}
}
}
long long work(){//开long long
topsort();
long long ans=1;//n位置的答案
int len=1;
a[1]=hei[n];
for(int i=n-1;i;i--){
int x=hei[i];
if(x>a[len]){
a[++len]=x;
ans+=len;
}
else{
int pos=lower_bound(a+1,a+len+1,x)-a;
//大于或等于,但hei值不重复,就可以不用upper_bound(…)
a[pos]=x;
ans+=pos;
}
}
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(last[a[i]])add(last[a[i]],i);//大的连向小的
if(a[i]>1)add(i,last[a[i]-1]);//注意是i不是a[i],是位置不是值
last[a[i]]=i;
}
printf("%lld",work());
return 0;
}
~ \ ( ≧ ▽ ≦ ) / ~