2018.9.18模拟赛

又是晚上考试mdzz

T1 以前做过qwqA掉了

T2

把max(a[i]-a[j])拆成max{a[i]}+max{-a[j]},分成两部分算

因为有很多区间的答案都一样,所以考虑a[i]可以是哪些区间的max

往左往右找到上一个比它大的位置,就可以找到它能覆盖的最长的l,r

ans+=a[i]*((r-i+1)*(i-l+1)-1)

找这个位置有人用nlogn过的,qwq但是这个可以O(n)!

就是用单调栈维护递减的序列,上一个比它大的就是top这个位置

我要diss这个数据范围!!QAQ考场好不容易写一次正解但是居然可以被nlogn过掉

 

放上我的考场代码!qvq

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cctype>
#define maxn 100005
#define LL long long
#define re register
using namespace std;
int t,n,a[maxn],stk[maxn],top,pre[maxn];
LL ans;

inline int rd(){
	int x=0,f=0; char ch=0;
	while(!isdigit(ch)) f|=ch=='-',ch=getchar();
	while( isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f?-x:x;
}

inline void init(){
	memset(stk,0,sizeof stk); top=0; memset(pre,0,sizeof pre);
}

inline LL solve(){
	init(); LL res=0;
	for(re int i=1;i<=n;i++){
		while(top>0 && a[stk[top]]<a[i]) top--;
		pre[i]=stk[top]+1;
		stk[++top]=i;
	}
	top=0; stk[0]=n+1;
	for(re int i=n;i;i--){
		while(top>0 && a[stk[top]]<a[i]) top--;
		int tmp=stk[top]-1; 
		res+=1LL*(1LL*(i-pre[i]+1)*(tmp-i+1)-1)*a[i];
		stk[++top]=i;
	}
	return res;
}

int main(){
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout); 
	t=rd();
	while(t--){
		n=rd();
		for(re int i=1;i<=n;i++) a[i]=rd();
		ans=solve();
		for(re int i=1;i<=n;i++) a[i]=-a[i];
		ans+=solve();
		printf("%lld\n",ans);
	}
	return 0;
}

T3

先放上给的正解和标程:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
ll read(){
	ll ans=0;
	char last=' ',ch=getchar();
	while(ch<'0' || ch>'9')last=ch,ch=getchar();
	while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
	if(last=='-')ans=-ans;
	return ans;
}
//head
#define N 110000
int head[N],Next[N*2],v[N*2],num,fa[N],son[N],sum[N],n,s[N],Fa[N];
ll ans=0,Sum,Sum2;
bool vis[N];
set<int>Q;
set<int>::iterator it,it1,it2;
int find(int x){
	if(x==Fa[x])return x;
	return Fa[x]=find(Fa[x]);
}
void add(int x,int y){
	v[++num]=y;Next[num]=head[x];head[x]=num;
}
void dfs1(int u){
	sum[u]=1;son[u]=0;
	for(int i=head[u];i;i=Next[i])
		if(v[i]!=fa[u]){
			fa[v[i]]=u;
			dfs1(v[i]);
			sum[u]+=sum[v[i]];
			if(!son[u] || sum[v[i]]>sum[son[u]])son[u]=v[i];
		}
}
ll cal(ll n){
	return n*(n-1)/2;
}
void Add(int u){
	Q.insert(u);
	it=Q.find(u);
	it1=it2=it;
	it1--;it2++;
	Sum2-=cal((*it2)-(*it1)-1);
	Sum2+=cal((*it)-(*it1)-1)+cal((*it2)-(*it)-1);
	vis[u]=1;
	if(vis[u-1]){
		int fx=find(u-1),fy=find(u);
		Sum+=(ll)s[fx]*s[fy];
		Fa[fx]=fy;
		s[fy]+=s[fx];
	}
	if(vis[u+1]){
		int fx=find(u+1),fy=find(u);
		Sum+=(ll)s[fx]*s[fy];
		Fa[fx]=fy;
		s[fy]+=s[fx];
	}
}
void bfs(int u){
	Add(u);
	for(int i=head[u];i;i=Next[i])
		if(v[i]!=fa[u])bfs(v[i]);
}
void clear(int u){
	s[u]=1;vis[u]=0;Fa[u]=u;
	for(int i=head[u];i;i=Next[i])
		if(v[i]!=fa[u])clear(v[i]);
}
void dfs2(int u){
	for(int i=head[u];i;i=Next[i])
		if(v[i]!=fa[u] && v[i]!=son[u]){
			dfs2(v[i]);
			clear(v[i]);
			Sum=0;
			Q.clear();
			Q.insert(0);
			Q.insert(n+1);
			Sum2=(ll)(n-1)*n/2;
		}

	if(son[u])dfs2(son[u]);
	for(int i=head[u];i;i=Next[i])
		if(v[i]!=fa[u] && v[i]!=son[u])bfs(v[i]);

	Add(u);
	
	ans+=(ll)n*(n-1)/2-Sum-Sum2;
}
int main(){
	freopen("treecnt.in","r",stdin);
	freopen("treecnt.out","w",stdout);
	n=read();
	Q.clear();
	Q.insert(0);
	Q.insert(n+1);
	Sum2=cal(n);
	rep(i,1,n)Fa[i]=i,s[i]=1,vis[i]=0;
	rep(i,2,n){
		int x=read(),y=read();
		add(x,y);
		add(y,x);
	}
	dfs1(1);
	dfs2(1);
	cout<<ans<<endl;
	return 0;
}

但是我们并没有看懂这个标程写的···有人看懂的话拜托教教窝qwq

 

于是就有了另外一种方法:

首先可以把这些路径分成1~2,2~3,···n-1~n

然后先不管重复的,都把这些路径的贡献加到答案里,比如l~r就是[l~l+1]+[l+1~l+2]+···+[r-1,r]

通过自己举例子什么的可以发现,i~i+1这条路径被用了i*(n-i)遍

(这个是因为以i为左端点有n-i个右端点可以选,然后1~i都要算一遍这些区间,所以是i*(n-i))

然后再考虑重复算的,为什么会有重复的呢,比如3在1和2的中间

在算1~3的时候实际上应该是1->3->2,而不是[1~2]+[2~3]

那么重复了多少次?如果这条边被i~i+1的路径覆盖了,那么在算这条边在j~j+1中的贡献的时候

它会多算i*(n-j)次,原理和上面的相似

那我们可以在遍历完i~i+1的路径算完答案之后给这些边打上i的标记

算答案的式子就是:

ans+=边数*(n-i)*i-(i~i+1)标记和*(n-i)

这个可以用树剖+线段树!orzhsz!!

放上我的代码qwq:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cctype>
#define N 100005
#define LL long long
#define ls cur<<1
#define rs cur<<1|1
using namespace std;
int n,cnt,head[N];
int siz[N],id[N],son[N],top[N],fa[N],dep[N],rk[N],tot; 
LL ans;

inline int rd(){
	int x=0,f=0; char ch=0;
	while(!isdigit(ch)) f|=ch=='-',ch=getchar();
	while( isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f?-x:x;
}

struct EDGE{
	int to,nxt;
}edge[N<<1];

inline void add(int x,int y){
	edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt;
}

inline void dfs1(int u,int fat,int depth){
	dep[u]=depth; fa[u]=fat; siz[u]=1;
	int maxson=-1;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to; if(v==fat) continue;
		dfs1(v,u,depth+1); siz[u]+=siz[v];
		if(siz[v]>maxson) son[u]=v,maxson=siz[v];
	} return;
}

inline void dfs2(int u,int t){
	top[u]=t; id[u]=++tot; rk[tot]=u;
	if(!son[u]) return;
	dfs2(son[u],t);
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(v!=son[u] && v!=fa[u]) dfs2(v,v);
	} return;
}

struct NODE{
	int l,r; LL sum,lazy;
}node[N<<2];

inline void pushup(int cur){
	node[cur].sum=node[ls].sum+node[rs].sum;
}

inline void build(int cur,int L,int R){
	if(L==R){
		node[cur].l=node[cur].r=L;
		node[cur].sum=0; node[cur].lazy=0;
		return;
	}
	int mid=(L+R)>>1;
	build(ls,L,mid); build(rs,mid+1,R);
	node[cur].l=node[ls].l; node[cur].r=node[rs].r;
	pushup(cur);
}

inline void pushdown(int cur){
	if(node[cur].lazy==0) return;
	node[ls].sum=node[cur].lazy*(node[ls].r-node[ls].l+1);
	node[rs].sum=node[cur].lazy*(node[rs].r-node[rs].l+1);
	node[ls].lazy=node[cur].lazy; node[rs].lazy=node[cur].lazy;
	node[cur].lazy=0;
}

inline void update(int cur,int L,int R,int c){
	if(L<=node[cur].l && node[cur].r<=R){
		node[cur].sum=c*(node[cur].r-node[cur].l+1);
		node[cur].lazy=c; return;
	}
	pushdown(cur);
	int mid=(node[cur].l+node[cur].r)>>1;
	if(L<=mid) update(ls,L,R,c);
	if(mid<R) update(rs,L,R,c);
	pushup(cur); 
}

inline LL query(int cur,int L,int R){
	if(L<=node[cur].l && node[cur].r<=R)
		return node[cur].sum;
	pushdown(cur);
	int mid=(node[cur].l+node[cur].r)>>1; LL res=0;
	if(L<=mid) res+=query(ls,L,R);
	if(mid<R) res+=query(rs,L,R);
	return res;
}

inline void change(int x,int y,int c){
	int fx=top[x],fy=top[y];
	while(fx!=fy){
		if(dep[fx]>=dep[fy]){
			update(1,id[fx],id[x],c);
			x=fa[fx];
		}
		else{
			update(1,id[fy],id[y],c);
			y=fa[fy];
		}
		fx=top[x],fy=top[y];
	}
	if(id[x]<=id[y]) update(1,id[x],id[y],c);
	else update(1,id[y],id[x],c); return;
}

inline LL ask(int x,int y){
	int fx=top[x],fy=top[y]; LL res=0;
	while(fx!=fy){
		if(dep[fx]>=dep[fy]){
			res+=query(1,id[fx],id[x]);
			x=fa[fx];
		}
		else{
			res+=query(1,id[fy],id[y]);
			y=fa[fy];
		}
		fx=top[x],fy=top[y];
	}
	if(id[x]<=id[y]) res+=query(1,id[x],id[y]);
	else res+=query(1,id[y],id[x]);
	return res;
}

inline int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]>=dep[top[y]]) x=fa[top[x]];
		else y=fa[top[y]];
	}
	return dep[x]>dep[y]?y:x;
}

inline void solve(){
	for(int i=1;i<n;i++){
		int x=LCA(i,i+1); LL y=ask(x,x);
		int tmp=dep[i]+dep[i+1]-2*dep[x];
		ans+=1LL*tmp*(n-i)*i-(ask(i,i+1)-ask(x,x))*(n-i);
		change(i,i+1,i); change(x,x,y);
	} return;
}

int main(){
//	freopen("treecnt.in","r",stdin);
//	freopen("treecnt.out","w",stdout);
	n=rd();
	for(int i=1;i<n;i++){
		int x=rd(),y=rd();
		add(x,y); add(y,x);
	}
	dfs1(1,1,1); dfs2(1,1);
	build(1,1,n); solve();
	printf("%lld\n",ans);
	return 0;
}

这个暴力分其实有80分qwq但是没有打,血亏!

 

最后得分是100+100+0=200  r4qwq被吊打嘤嘤嘤

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值