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; }
期望得分: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; }
正解:因为排列是随机的,所以从每个点向后可能的差值最多 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; }
期望得分: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; }