题解第五届图灵杯
Posted Qiuly
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解第五届图灵杯相关的知识,希望对你有一定的参考价值。
// created on 23.05.13
A. 差值
考虑求长度为 \\(i\\) 的答案,肯定是长度 \\(\\geq i\\) 的后缀,按照字典序排序后,相邻两个求解。
所以考虑倒着扫,每次对于 \\(i\\) 的后缀找到排名前后第一个后缀,求出两个长度为 \\(i\\) 的解。更新解(比大小)的部分用哈希和二分解决即可。
然后最后只要倒着贡献,用 \\(i+1\\) 的最优解贡献 \\(i\\) 即可(非 \\(i+1\\) 最有解一定无用)。
时间复杂度 \\(O(n\\log n)\\) 。
B. 基础循环结构练习题
考虑非严格递增的部分,令 \\(d_i=b_i-b_i-1\\),枚举 \\(i\\) 从 \\(n\\) 到 \\(1\\),设计如下策略:
- 此时 \\(i\\) 是全局最大值,通过一次 mod 将其变为 \\(0\\) 。
- 全局加 \\(d_i\\) 。
注意到每次 mod 不会影响后缀,并且一个位置最终的值就是前侧 \\(\\sum d\\) 。我们用 \\(2n\\) 次操作达到了目的。
如果非递增的话,考虑最后一步的 mod 。倒着做,相当于用 \\(c=\\max b+1\\) 来调整 \\(b\\) 使得其为非严格递增即可。恰好只要多一次操作。
C. 基础计算几何练习题
观察一下,我们将点对按照点对间距离排序,每次取出距离最小的点对集合 \\(S=\\(x_1,y_1),(x_2,y_2),\\cdots\\\\),然后对于 \\(x_i,y_i\\),如果之前两者答案都是 false
,那么一起改成 true
。
怎么观察出来的?从链考虑,先考虑间隔最小的,肯定一步到位全是 true
。然后是次小的,如果和最小不相交也是 true
了,否则肯定不操作,不然就输了。
那么至此可以写出 50pts 。
我们用 KDT 维护每个点与周围 未删除点 的最小距离,并打包成二元组存入堆。每次取出堆头,检查最小距离是否是对的(可能与之形成最小距离的点已经被删了)。在踢掉所有非法的最小距离后,就得到了当前全局最小距离。接着不断地取出,拥有全局最小距离的点,并且这一轮我们会将这些点全部删除。如此往复直到点剩下小于两个。
时间复杂度未知(我们只需要注意一个点被错误取出的次数,感受一下这个次数很小,不知道能不能和平面最近点对一样分析?),但其实速度还行。
但其实有更好的做法:将网格划分成若干个 \\(2^i\\times 2^i\\) 的格子,每次一个格子里只剩下至多一个点,查询时只需要考虑格子周围的格子中的点即可。
即,从 \\(i=0\\) 开始做,每次将 \\(O(n)\\) 个平面最近点对拿出来按照距离排序,然后按照暴力的方式模拟即可。此时 \\(2^i+1\\times 2^i+1\\) 的格子中至多剩下一个点,否则在这一步肯定消掉了。于是,我们得到了 \\(O(n\\log V)\\) 的做法。
D. 永恒悲剧
对于一个位置 \\(1\\leq i\\leq n\\) 而言,一个右端点 \\(r\\) 存在 \\(p\\leq r\\) 满足合法左端点为 \\(l\\leq p\\) 且稳定值一样。
\\(p\\) 的要求是:可以在 \\([p+1,r]\\) 中找到一个,opt
不同操作相交的位置 \\(p\'\\) 。我们记 \\(nxt_p\\) 表示 \\(p\\) 后第一个满足要求的位置,\\(lst_p\\) 表示 \\(p\\) 前第一个满足要求的位置。
那么对于 \\(r\\) 而言有 \\(p=\\max\\lst_1,lst_2,\\cdots,lst_r\\\\) 。当有多个 \\(i\\) 的 \\(lst_i\\) 相同时,显然只要保留最小的 \\(i\\) 。此时若保留的 \\(i\\) 为 \\(t\\),那么 \\(nxt_lst_t=t\\) 也是必然成立的。对于 \\(i,j\\) 若 \\(lst_i=j,nxt_j=i\\) 就记 \\((j,i)\\) 为一个 "稳定对" 。
不妨假设 \\(p\\) 是 max
操作,那么最后 \\(r\\) 得到的贡献为 \\(p\\times \\min\\c_p+1,\\cdots,c_r\\\\) 。
外层直接上线段树分治,修改 / 撤销 \\(lst_i,nxt_i\\):每次插入 \\(i\\) 只要找到 \\(lst_i,nxt_i\\) 并检查是否形成 "稳定对" 即可。到了叶子就做一遍贡献(即对 \\(ans_1,ans_2,\\cdots,ans_r\\) 贡献答案)。
于是问题来到怎么处理答案的贡献。考虑用线段树维护,并定义如下函数:
- \\(\\mathbfpush(x,l,r,p,lim_c,c)\\) 表示:\\([x,l,r]\\) 节点,前侧 \\(lst_i\\) 的最大值为 \\(p\\),\\(p\\) 后续 \\(c\\) 的最小值(或最大值)为 \\(lim_c\\) 。一共要对 \\(ans_i,l\\leq i\\leq r\\) 贡献 \\(c\\) 次。
- \\(\\mathbfpush_fix_p(x,l,r,type,lim_c,c)\\) 表示:在 \\(p\\) 已经不会在 \\([x,l,r]\\) 中改变时,\\(type\\) 表示 \\(p\\) 的类型,\\(lim_c\\) 同理,\\(c\\) 同理(为了方便,可以把 \\(p\\) 乘入 \\(c\\) 中)。
在一次 \\(\\mathbfpush(x,l,r,p,lim_c,c)\\) 中:
- 如果到了叶子,更新并直接算贡献。
- 如果左侧 \\(\\max lst_i\\) 比 \\(p\\) 大,那么往右侧打标记表示 "右侧节点被计算 \\(c\\) 次贡献"。此时 \\(p,lim_c\\) 不会影响右侧节点,递归进入左侧。
- 否则,左侧区间中 \\(p\\) 不会改变,用 \\(\\mathbfpush_fix_p\\) 递归计算贡献。然后再递归进入右侧继续计算。
在一次 \\(\\mathbfpush_fix_p(x,l,r,type,lim_c,c)\\) 中:
- 如果到了叶子,更新并直接算贡献。
- 如果左侧对 \\(lim_c\\) 没有影响,那么所有位置都加上 \\(lim_c\\times c\\),打标记处理。
- 否则,递归进入左侧计算。此时到右侧时,\\(lim_c\\) 只取决于左侧,所以可以在 \\(x\\) 打标记表示 " \\(type\\) 固定,用左侧对应 \\(lim_c\\),递归进入右侧贡献系数加上 \\(c\\) "。
\\(\\mathbfpush_fix_p\\) 是 \\(O(\\log m)\\) 的,\\(\\mathbfpush\\) 是 \\(O(\\log^2 m)\\) 的。一共 \\(O(m\\log n)\\) 次单点修改,于是 pushdown 的次数是 \\(O(m\\log n\\log m)\\) 。每次 pushdown 需要根据标记用 \\(\\mathbfpush,\\mathbfpush_fix_p\\) 计算代价,那么复杂度是 \\(O(m\\log n\\log^3 m)\\) 的,但是常数较小。
//
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(...) fprintf (stderr, __VA_ARGS__)
#define fi first
#define se second
#define lep(i, l, r) for (int i = (l), i##_end = (r); i <= i##_end; ++ i)
#define rep(i, r, l) for (int i = (r), i##_end = (l); i >= i##_end; -- i)
char _c; bool _f; template <class T> inline void IN (T & x)
x = 0, _f = 0; while (_c = getchar (), ! isdigit (_c)) if (_c == \'-\') _f = 1;
while (isdigit (_c)) x = x * 10 + _c - \'0\', _c = getchar (); if (_f) x = - x;
template <class T> inline void chkmin (T & x, T y) if (x > y) x = y;
template <class T> inline void chkmax (T & x, T y) if (x < y) x = y;
const int N = 1e5 + 5;
const int INF = 1e9;
int n, m;
ll ans[N];
pair <int, int> opt[N];
// segment tree
#define lc ((x) << 1)
#define rc ((x) << 1 | 1)
#define mid ((l + r) >> 1)
bool act[N];
struct node
int mi_c, mx_c, p, p_c;
ll l_to_r, fix_p[2], ans;
t[N << 2];
void build (int x, int l, int r)
t[x].mi_c = INF, t[x].mx_c = -1;
if (l == r) return t[x].p_c = opt[l].fi, void ();
build (lc, l, mid), build (rc, mid + 1, r);
inline void pushup (int x)
t[x].mi_c = min (t[lc].mi_c, t[rc].mi_c);
t[x].mx_c = max (t[lc].mx_c, t[rc].mx_c);
if (t[lc].p > t[rc].p)
t[x].p = t[lc].p;
if (opt[t[x].p].se == 1) t[x].p_c = min (t[lc].p_c, t[rc].mi_c);
if (opt[t[x].p].se == 2) t[x].p_c = max (t[lc].p_c, t[rc].mx_c);
else
t[x].p = t[rc].p, t[x].p_c = t[rc].p_c;
// pushdown
void push_fix_p (int x, int l, int r, int type, int p_c, ll buf)
if (l == r)
if (act[l])
if (type == 1 && opt[l].se == 2) chkmin (p_c, opt[l].fi);
if (type == 2 && opt[l].se == 1) chkmax (p_c, opt[l].fi);
ans[l] += 1ll * p_c * buf;
else
if ((type == 1 && t[lc].mi_c >= p_c) || (type == 2 && t[lc].mx_c <= p_c))
t[lc].ans += 1ll * p_c * buf;
push_fix_p (rc, mid + 1, r, type, p_c, buf);
else
t[x].fix_p[type - 1] += buf;
push_fix_p (lc, l, mid, type, p_c, buf);
void push (int x, int l, int r, int p, int p_c, int buf)
if (l == r)
if (act[l])
if (t[x].p > p) p = t[x].p, p_c = t[x].p_c;
else
if (opt[p].se == 1 && opt[l].se == 2) chkmin (p_c, opt[l].fi);
if (opt[p].se == 2 && opt[l].se == 1) chkmax (p_c, opt[l].fi);
ans[l] += 1ll * p * p_c * buf;
else
if (t[lc].p >= p)
t[x].l_to_r += buf;
push (lc, l, mid, p, p_c, buf);
else
if (p) push_fix_p (lc, l, mid, opt[p].se, p_c, 1ll * buf * p);
if (! p) push (rc, mid + 1, r, p, p_c, buf);
else
if (opt[p].se == 1) push (rc, mid + 1, r, p, min (p_c, t[lc].mi_c), buf);
if (opt[p].se == 2) push (rc, mid + 1, r, p, max (p_c, t[lc].mx_c), buf);
inline void pushdown (int x, int l, int r)
if (t[x].l_to_r)
push (rc, mid + 1, r, t[lc].p, t[lc].p_c, t[x].l_to_r);
t[x].l_to_r = 0;
lep (o, 1, 2) if (t[x].fix_p[o - 1])
push_fix_p (rc, mid + 1, r, o, (o == 1 ? t[lc].mi_c : t[lc].mx_c), t[x].fix_p[o - 1]);
t[x].fix_p[o - 1] = 0;
//
int lst[N], nxt[N];
void update (int x, int l, int r, int i)
if (l == r) return t[x].p = lst[i], void ();
pushdown (x, l, r);
i <= mid ? update (lc, l, mid, i) : update (rc, mid + 1, r, i);
pushup (x);
void flip (int x, int l, int r, int i)
if (l == r)
act[i] ^= 1;
if (opt[i].se == 1) t[x].mx_c = act[i] ? opt[i].fi : -1;
if (opt[i].se == 2) t[x].mi_c = act[i] ? opt[i].fi : INF;
else
pushdown (x, l, r);
i <= mid ? flip (lc, l, mid, i) : flip (rc, mid + 1, r, i);
pushup (x);
/* lst and nxt */
int find_lst (int x, int l, int r, int i)
if (l > i) return -1;
if (opt[i].se == 1 && t[x].mi_c > opt[i].fi) return -1;
if (opt[i].se == 2 && t[x].mx_c < opt[i].fi) return -1;
if (l == r) return l;
int ret = find_lst (rc, mid + 1, r, i);
return ~ ret ? ret : find_lst (lc, l, mid, i);
int find_nxt (int x, int l, int r, int i)
if (r < i) return -1;
if (opt[i].se == 1 && t[x].mi_c > opt[i].fi) return -1;
if (opt[i].se == 2 && t[x].mx_c < opt[i].fi) return -1;
if (l == r) return l;
int ret = find_nxt (lc, l, mid, i);
return ~ ret ? ret : find_nxt (rc, mid + 1, r, i);
vector <pair <int, int> > s_nxt[21], s_lst[21];
inline void find_lst (int i, int & dep)
int p = find_lst (1, 1, m, i);
if ( ~ p && find_nxt (1, 1, m, p) == i)
if (nxt[p])
s_lst[dep].emplace_back ( nxt[p], p );
lst[nxt[p]] = 0, update (1, 1, m, nxt[p]);
s_nxt[dep].emplace_back ( p, nxt[p] ), nxt[p] = i;
s_lst[dep].emplace_back ( i, 0 ), lst[i] = p, update (1, 1, m, i);
inline void find_nxt (int i, int & dep)
int p = find_nxt (1, 1, m, i);
if ( ~ p && find_lst (1, 1, m, p) == i)
if (lst[p])
s_nxt[dep].emplace_back ( lst[p], p ), nxt[lst[p]] = 0;
s_nxt[dep].emplace_back ( i, 0 ), nxt[i] = p;
s_lst[dep].emplace_back ( p, lst[p] ), lst[p] = i, update (1, 1, m, p);
inline void join (int i, int & dep)
flip (1, 1, m, i);
find_lst (i, dep), find_nxt (i, dep);
void collect (int x, int l, int r)
if (l == r) return ans[l] += t[x].ans, void ();
pushdown (x, l, r);
if (t[x].ans) t[lc].ans += t[x].ans, t[rc].ans += t[x].ans;
collect (lc, l, mid), collect (rc, mid + 1, r);
#undef lc
#undef rc
#undef mid
//
// divide
#define lc ((x) << 1)
#define rc ((x) << 1 | 1)
#define mid ((l + r) >> 1)
vector <int> stk[N << 2];
void insert (int x, int l, int r, int L, int R, int i)
if (L <= l && r <= R) return stk[x].emplace_back (i), void ();
if (L <= mid) insert (lc, l, mid, L, R, i);
if (R > mid) insert (rc, mid + 1, r, L, R, i);
void divide (int x, int l, int r, int dep = 0)
for (auto & i : stk[x]) join (i, dep);
if (l == r) push (1, 1, m, 0, 0, 1);
else
divide (lc, l, mid, dep + 1);
divide (rc, mid + 1, r, dep + 1);
for (auto & i : stk[x]) flip (1, 1, m, i);
reverse (s_nxt[dep].begin (), s_nxt[dep].end ());
reverse (s_lst[dep].begin (), s_lst[dep].end ());
for (auto & [a, b] : s_nxt[dep]) nxt[a] = b;
for (auto & [a, b] : s_lst[dep]) lst[a] = b, update (1, 1, m, a);
s_nxt[dep].clear ();
s_lst[dep].clear ();
#undef lc
#undef rc
#undef mid
//
signed main ()
IN (n), IN (m);
for (int op, l, r, c, i = 1; i <= m; ++ i)
IN (op), IN (l), IN (r), IN (c);
opt[i] = c, op ;
insert (1, 1, n, l, r, i);
build (1, 1, m);
divide (1, 1, n);
collect (1, 1, m);
lep (i, 1, m) printf ("%lld%c", ans[i], " \\n"[i == m]);
return 0;
第九届“图灵杯”NEUQ-ACM程序设计竞赛个人题解
https://ac.nowcoder.com/acm/contest/27302#question
目录
- 大学期末现状【签到】
- G1024【签到】
- NEUQ【模拟】
- 小G的任务【签到】
- nn与游戏【bfs】
- 第二大数【第二大】
- Num【思维】
- 特征值【找规律 模拟】
- 最大公约数【思维】
- 玄神的字符串【贪心】
- WireConnection【最小生成树】
大学期末现状【签到】
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
int main()
int x; cin>>x;
if(x>=60) puts("jige,haoye!");
else puts("laoshi,caicai,laolao");
return 0;
G1024【签到】
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
int main()
int n,k; cin>>n>>k;
for(int i=1;i<=k;i++)
int a,b; cin>>a>>b;
if(b-a>=n)
cout<<i;
return 0;
puts("G!");
return 0;
NEUQ【模拟】
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
string ss="NEUQ";
int main()
int n; cin>>n;
string s; cin>>s;
int cnt=s.size();
for(int i=0,j=0;i<s.size();i++)
if(s[i]==ss[j]) j++;
if(j==4) j=0,cnt-=4;
cout<<cnt;
return 0;
小G的任务【签到】
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
LL n,sum;
int main()
cin>>n;
for(int i=1;i<=n;i++)
LL temp=i;
while(temp) sum+=temp%10,temp/=10;
cout<<sum;
return 0;
nn与游戏【bfs】
#include<bits/stdc++.h>
using namespace std;
const int N=2010;
int a[N][N],st[N][N],n,m;
int dx[4]=-1,0,0,1;
int dy[4]=0,-1,1,0;
int bfs(int x,int y,int xx,int yy)
memset(st,-1,sizeof st);
st[x][y]=0;
queue< pair<int,int> >q; q.push(x,y);
while(q.size())
auto temp=q.front(); q.pop();
x=temp.first,y=temp.second;
if(x==xx&&y==yy) return st[x][y];
for(int i=0;i<4;i++)
int tempx=x+dx[i],tempy=y+dy[i];
if(tempx<0||tempx>=n||tempy<0||tempy>=n) continue;
if(st[tempx][tempy]!=-1) continue;
if(a[tempx][tempy]) continue;
st[tempx][tempy]=st[x][y]+1;
q.push(tempx,tempy);
return -1;
int main(void)
cin>>n>>m;
for(int i=1;i<=m;i++)
int x,y; cin>>x>>y;
a[x][y]=1;
int t; cin>>t;
vector< pair<int,int> >ve1,ve2;
while(t--)
int x,y,xx,yy; cin>>x>>y>>xx>>yy;
a[x][y]=1,a[xx][yy]=1;
ve1.push_back(x,y);
ve2.push_back(xx,yy);
for(int i=0;i<ve1.size();i++)
int x=ve1[i].first,y=ve1[i].second;
int xx=ve2[i].first,yy=ve2[i].second;
a[xx][yy]=0;
cout<<bfs(x,y,xx,yy)<<endl;
a[xx][yy]=1;
return 0;
第二大数【第二大】
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
typedef long long int LL;
LL a[N],n,sum;
int main(void)
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)
LL d1=a[i],d2=0;
for(int j=i+1;j<=n;j++)
if(a[j]>=d1) d2=d1,d1=a[j];
else if(a[j]>d2) d2=a[j];
sum+=d2;
cout<<sum;
return 0;
Num【思维】
我们先不考虑(a+b)的值,假如n=a*b 则我们枚举到sqrt(n)即可。加上了(a+b)多了点值,我们多加一些即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
int main()
LL n; cin>>n;
bool flag=0;
for(LL i=1;i<=sqrt(n)+5;i++) //枚举a
LL a=i;
LL b=(n-a)/(a+1);//计算b
LL sum=a*b+a+b;
if(sum==n&&b) flag=1;//满足
if(flag) puts("Yes");
else puts("No");
return 0;
评论区的思路。
特征值【找规律 模拟】
#include<bits/stdc++.h>
using namespace std;
vector<int>A,B,C;
const int N=1e5*6+10;
int s[N],ans[N];
int main(void)
string a; cin>>a;
int n=a.size();
a="0"+a;
for(int i=1;i<=n;i++) s[i]=s[i-1]+(a[i]-'0');
for(int i=1;i<=n;i++) ans[i]=s[n+1-i];
for(int i=1;i<=n;i++) ans[i+1]+=ans[i]/10,ans[i]=ans[i]%10;
if(ans[n+1]) cout<<ans[n+1];
for(int i=n;i>=1;i--) cout<<ans[i];
return 0;
最大公约数【思维】
评论区里的思路。
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
int gcd(int a,int b)return b?gcd(b,a%b):a;
const int N=1e5+10;
LL a[N],n,sum;
LL solve(LL x)
int cnt=0;
for(LL i=1;i<=x/i;i++)
if(x%i==0)
cnt++;
if(x/i!=i) cnt++;
return cnt;
int main(void)
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],sum+=a[i];
cout<<solve(sum);
return 0;
玄神的字符串【贪心】
分类讨论即可。
#include<bits/stdc++.h>
using namespace std;
int main(void)
string s; cin>>s;
int a,b,c; cin>>a>>b>>c;
int cnt0=0,cnt1=0;
for(int i=0;i<s.size();i++)
if(s[i]=='0') cnt0++;
else cnt1++;
int sum=0;
if(a<=b)//01 比单独的00 11优惠
int temp=min(cnt1,cnt0);
sum+=temp*a;
cnt1-=temp,cnt0-=temp;
sum+=max(cnt1,cnt0)*(min(b,a+c))/2;
else//00 11搭配
sum=( (cnt1/2)+(cnt0/2) ) * b;
cnt1=cnt1%2,cnt0=cnt0%2;
if(cnt1&&cnt0) sum+=min(a,c+b);
cout<<sum;
return 0;
WireConnection【最小生成树】
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int N=1e5+10;
struct nodeLL x,y,z;;
bool cmp(node a,node b)return a.z<b.z;
int n,p[N];
vector<node>ve,ans;
int find(int x)
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
void solve()
for(int i=1;i<=ve.size();i++) p[i]=i;
sort(ans.begin(),ans.end(),cmp);
LL sum=0;
for(int i=0;i<ans.size();i++)
int a=ans[i].x,b=ans[i].y,c=ans[i].z;
if(find(a)==find(b)) continue;
p[find(a)]=find(b);
sum+=c;
cout<<sum;
int main(void)
int n; cin>>n;
while(n--)
int x,y,z; cin>>x>>y>>z;
ve.push_back(x,y,z);
for(int i=0;i<ve.size();i++)
LL x=ve[i].x,y=ve[i].y,z=ve[i].z;
for(int j=i+1;j<ve.size();j++)
LL xx=ve[j].x,yy=ve[j].y,zz=ve[j].z;
LL c=以上是关于题解第五届图灵杯的主要内容,如果未能解决你的问题,请参考以下文章