18/9/21模拟赛-Updated

        18/9/21模拟赛

 

期望得分:100;实际得分:0  qwq

拿到题目第一眼,我去,这不是洛谷原题(仓鼠找Sugar)吗

又多看了几眼,嗯,对,除了是有多组数据外,就是原题

然后码码码。。。。自以为写的很对 qwq

评测结束后。。。为什么我T1没有输出啊啊啊。。。

经某童鞋帮忙,发现

第一次被文件输入输出坑 qwqwq。。。

加上后就A了,白丢100 pts 蓝瘦

思路:树剖分别求LCA,然后判断LCA是否在另一条路径上

不要忘记清空数组!

#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int M = 100005;
int T, n, m, tot;
int to[M << 1], net[M << 1], head[M];
int deep[M], top[M], dad[M], size[M];

void add(int u, int v) {
    to[++tot] = v; net[tot] = head[u]; head[u] = tot;
    to[++tot] = u; net[tot] = head[v]; head[v] = tot;
}

void dfs(int now) {
    size[now] = 1;
    deep[now] = deep[dad[now]] + 1;
    for (int i = head[now]; i; i = net[i])
        if (to[i] != dad[now]) {
            dad[to[i]] = now;
            dfs(to[i]);
            size[now] += size[to[i]];
        }
}

void dfsl(int now) {
    int t = 0;
    if (!top[now]) top[now] = now;
    for (int i = head[now]; i; i = net[i])
        if (to[i] != dad[now] && size[to[i]] > size[t])
            t = to[i];
    if (t) {
        top[t] = top[now];
        dfsl(t);
    }
    for (int i = head[now]; i; i = net[i])
        if (to[i] != dad[now] && to[i] != t)
            dfsl(to[i]);
}

int lca(int x, int y) {
    while (top[x] != top[y]) {
        if (deep[top[x]] < deep[top[y]])
            swap(x, y);
        x = dad[top[x]];
    }
    return deep[x] > deep[y] ? y : x;
}

void clear() {
    tot = 0;
    memset(to, 0, sizeof to);
    memset(net, 0, sizeof net);
    memset(dad, 0, sizeof dad);
    memset(top, 0, sizeof top);
    memset(head, 0, sizeof head);
    memset(deep, 0, sizeof deep);
    memset(size, 0, sizeof size);
}

int main() {
    freopen("railway.in","r",stdin);
    freopen("railway.out","w",stdout);
    scanf("%d", &T);
    for (int q = 1; q <= T; ++q) {
        if (q != 1) clear();
        scanf("%d%d", &n, &m);
        for (int i = 1; i < n; ++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            add(u, v);
        }
        dfs(1);
        dfsl(1);
        for (int i = 1; i <= m; ++i) {
            int a, b, c, d;
            scanf("%d%d%d%d", &a, &b, &c, &d);
            int S = lca(a, b);
            int R = lca(c, d);
            if (S < R) {
                swap(S, R);
                swap(a, c);
                swap(b, d);
            }
            if (lca(S, c) == S || lca(S, d) == S) printf("YES\n");
            else printf("NO\n");
        }
    }
    fclose(stdin); fclose(stdout);
    return 0;
}
View Code

 

 

期望得分:100? 实际得分:20  (那80pts的都TLE了。。。)

思路:线段树维护区间的最大最小值,然后枚举每一个区间求解

事实证明,我真的不会算时间复杂度 qwq

#include <algorithm>
#include <cstring>
#include <cstdio>

#ifdef WIN32
#define LL "%I64d\n"
#else
#define LL "%lld\n"
#endif

using namespace std;
typedef long long Ll;
const int M = 100005;
int n, m;
Ll ans;
int ll[M << 2], rr[M << 2];
int maxn[M << 2], minn[M << 2];

void update(int now) {
    maxn[now] = max(maxn[now << 1], maxn[now << 1 | 1]);
    minn[now] = min(minn[now << 1], minn[now << 1 | 1]);
}

void build(int now, int l, int r) {
    ll[now] = l; rr[now] = r;
    if (l == r) {
        scanf("%d", &maxn[now]);
        minn[now] = maxn[now];
        return ;
    }
    int mid = (l + r) >> 1;
    build(now << 1, l, mid);
    build(now << 1 | 1, mid + 1, r);
    update(now);
}

int query(int now, int l, int r) {
    if (ll[now] == l && rr[now] == r)
        return maxn[now];
    int mid = (ll[now] + rr[now]) >> 1;
    if (l <= mid && mid < r) return max(query(now << 1, l, mid), query(now << 1 | 1, mid + 1, r));
    else if (r <= mid) return query(now << 1, l, r);
    else return query(now << 1 | 1, l, r);
}

int found(int now, int l, int r) {
    if (ll[now] == l && rr[now] == r)
        return minn[now];
    int mid = (ll[now] + rr[now]) >> 1;
    if (l <= mid && mid < r) return min(found(now << 1, l, mid), found(now << 1 | 1, mid + 1, r));
    else if (r <= mid) return found(now << 1, l, r);
    else return found(now << 1 | 1, l, r);
}

void clear() {
    ans = 0;
    memset(ll, 0, sizeof ll);
    memset(rr, 0, sizeof rr);
    memset(maxn, 0, sizeof maxn);
    memset(minn, 0, sizeof minn);
}

int main() {
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    scanf("%d", &m);
    for (int q = 1; q <= m; ++q) {
        if (q != 1) clear();
        scanf("%d", &n);
        build(1, 1, n);
        for (int i = 1; i <= n; ++i)
            for (int j = i + 1; j <= n; ++j)
                ans += (query(1, i, j) - found(1, i, j));
        printf(LL, ans);
    }
    fclose(stdin); fclose(stdout);
    return 0;
}
考场20pts代码

正解:因为排列是随机的,所以从每个点向后可能的差值最多 2logn 个,所以答案最多只可能有nlogn 种,用单调队列找出来统计即可

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pr;
const double pi=acos(-1);
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define Rep(i,u) for(int i=head[u];i;i=Next[i])
#define clr(a) memset(a,0,sizeof a)
#define pb push_back
#define mp make_pair
#define putk() putchar(' ')
ld eps=1e-9;
ll pp=1000000007;
ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;}
ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;}
ll gcd(ll a,ll b){return (!b)?a:gcd(b,a%b);}
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;
}
void put(ll a){
    if(a<0)putchar('-'),a=-a;
    int top=0,q[20];
    while(a)q[++top]=a%10,a/=10;
    top=max(top,1);
    while(top--)putchar('0'+q[top+1]);
}
//head
#define N 210000
int n,a[N],f1[N],f2[N],q[N];
ll s[N];
void solved(){
    n=read();
    rep(i,1,n)a[i]=read();
    a[n+1]=n+1;
    q[1]=n+1;
    int t=1;
    per(i,n,1){
        while(t && a[q[t]]<=a[i])--t;
        f1[i]=q[t];
        q[++t]=i;
    }
    a[n+1]=0;
    q[1]=n+1;
    t=1;
    per(i,n,1){
        while(t && a[q[t]]>=a[i])--t;
        f2[i]=q[t];
        q[++t]=i;
    }
    rep(i,0,n)s[i]=0;
    rep(i,1,n){
        int z1=i,z2=i;
        while(z1!=n+1  && z2!=n+1){
            if(f1[z1]<=f2[z2]){
                int tt=max(z1,z2),zz=a[z1]-a[z2];
                z1=f1[z1];
                s[zz]+=max(z1,z2)-tt;
            }
            else{
                int tt=max(z1,z2),zz=a[z1]-a[z2];
                z2=f2[z2];
                s[zz]+=max(z1,z2)-tt;
            }
        }
    }
    ll ans=0;
    rep(i,1,n-1)
        ans+=s[i]*i;
    cout<<ans<<endl;
}
int main(){
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    int T=read();
    while(T--){
        solved();
    }
    return 0;
}
正解

 比题解更好的做法  ——By lgj

分别求所有区间的最大值和、最小值和

用单调栈维护,用sum记录已经求过的区间的最大值的和

求最小值时,可将所有的数转为负数,重新跑一遍即可

最后将两次求出的值相加即可

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#define LL long long 
using namespace std;
const LL MAXN = 1e5 + 10;
inline LL read() {
    char c = getchar(); LL x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
LL T, N;
LL a[MAXN], q[MAXN];
LL solve() {
    LL h = 1, t = 0, ans = 0, sum = 0;
    for(LL i = 1; i <= N; i++) {
        while(h <= t && a[i] > a[q[t]]) sum -= a[q[t]] * (q[t] - q[t - 1]), t--;
        q[++t] = i;
        ans += a[i] * (q[t] - q[t - 1]) + sum;
        sum += a[i] * (q[t] - q[t - 1]);
    }
    return ans;
}
int main() {
    freopen("count.in", "r", stdin);
    freopen("count.out", "w", stdout);
    T = read();
    while(T--) {
        N = read();
        for(LL i = 1; i <= N; i++) a[i] = read();
        LL ans = solve();
        for(LL i = 1; i <= N; i++) a[i] = -a[i];
        LL ans2 = solve();
        cout << ans + ans2 << endl;
    }
    return 0;
}
% lgj dalao

 

 

期望得分:0;实际得分:0

一开始以为样例错了,死活推不出来

前一个小时做了前两题,剩下的时间大部分就在推样例发呆中度过了

最后半小时才发现读错题 qwq

然而暴力也写不完了。。。

正解:对于每一条边统计有多少个区间跨过这条边即可

统计这一问题的对偶问题,有多少个区间没跨过会更方便

 使用启发式合并+并查集统计子树内的,使用启发式合并+set 统计子树外的

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pr;
const double pi=acos(-1);
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define Rep(i,u) for(int i=head[u];i;i=Next[i])
#define clr(a) memset(a,0,sizeof a)
#define pb push_back
#define mp make_pair
ld eps=1e-9;
ll pp=1000000007;
ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;}
ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;}
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;
}
正解

 

  

posted @ 2018-09-21 14:53  落云小师妹  阅读(209)  评论(0编辑  收藏  举报