又是晚上考试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被吊打嘤嘤嘤