题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=102504#overview
这套题放了很久一直没写,直到我终于发现,现在已经不得不练数据结构的时候了,于是把这套题翻了出来开始写,嘛……总之感谢UESTC的题目先。估计全部写完大概要一周的时间,最近又因为期末考试没什么时间,大概会拖很久吧……
【UESTC1059】秋实大哥与小朋友
题意为对一个连续区间[1,n],有两种操作,将某一段区间的值增加v,或者查询某一个点的值。题目中给定的n很大,需要使用离散化,修改和查询使用线段树和树状数组完成均可,使用树状数组时,是“修改区间,查询点”的一种使用方式,具体操作为,在增加的时候,执行add(l,v)和add(r+1,-v),使[1,l-1]和[r+1,n]的值不变,只改变[l,r]区间的值。查询的时候执行query(x),统计x节点的值,类似于统计覆盖线段的一种做法。除此之外我还查到另一种解法……等我看懂了把代码贴上来。
#include<iostream> #include<cstdio> #include<map> #include<set> #include<vector> #include<stack> #include<queue> #include<string> #include<cstring> #include<sstream> #include<algorithm> #include<cmath> #define INF 0x3f3f3f3f #define eps 1e-8 #define pi acos(-1.0) using namespace std; typedef long long LL; struct Act{ int k,l,r; LL v; }; map<int,int> dir; vector<int> sav; Act a[100100]; LL t[300100]; int n,m,cnt; int lowbit(int k) { return k&(-k); } void add(int k,LL x) { while (k <= cnt) { t[k] += x; k += lowbit(k); } } LL query(int k) { LL ans = 0; while (k > 0) { ans += t[k]; k -= lowbit(k); } return ans; } int main() { while (scanf("%d%d",&n,&m) != EOF) { dir.clear(); memset(t,0,sizeof(t)); sav.clear();sav.push_back(-1); for (int i = 1;i <= m; i++) { scanf("%d",&a[i].k); if (!a[i].k) { scanf("%d%d%lld",&a[i].l,&a[i].r,&a[i].v); sav.push_back(a[i].l);sav.push_back(a[i].r); } else { scanf("%d",&a[i].l); sav.push_back(a[i].l); } } sort(sav.begin(),sav.end());cnt = 0; for (int i = 1;i < sav.size();i++) if (sav[i] != sav[i-1]) dir[sav[i]] = ++cnt; for (int i = 1;i <= m; i++) { if (!a[i].k) { //cout << dir[a[i].l] << ":" << dir[a[i].r] << endl; add(dir[a[i].l],a[i].v); add(dir[a[i].r]+1,-a[i].v); } else { //cout << dir[a[i].v] << endl; printf("%lld\n",query(dir[a[i].l])); } } } return 0; }
【UESTC1057】秋实大哥与花
对于给定区间完成区间修改和区间查询,线段树的基本应用。
#include<iostream> #include<cstdio> #include<map> #include<set> #include<vector> #include<stack> #include<queue> #include<string> #include<cstring> #include<sstream> #include<algorithm> #include<cmath> #define INF 0x3f3f3f3f #define eps 1e-8 #define pi acos(-1.0) using namespace std; typedef long long LL; LL t[400100],lazy[400100]; void down(int l,int r,int k) { int mid = (l + r) >> 1; lazy[k<<1] += lazy[k];t[k<<1] += (mid-l+1)*lazy[k]; lazy[(k<<1)|1] += lazy[k];t[(k<<1)|1] += (r-mid)*lazy[k]; lazy[k] = 0; } void build(int l,int r,int k) { if (l == r) { lazy[k] = 0; scanf("%lld",&t[k]); return; } int mid = (l + r) >> 1; build(l,mid,k<<1); build(mid+1,r,(k<<1)|1); t[k] = t[k<<1] + t[(k<<1)|1]; } void updata(int l,int r,int k,int x,int y,LL val) { //printf("%d %d %d\n",l,r,k); if (x <= l && r <= y) { lazy[k] += val; t[k] += (r-l+1)*val; return; } if (lazy[k]) down(l,r,k); int mid = (l+r) >> 1; if (x <= mid) updata(l,mid,k<<1,x,y,val); if (y > mid) updata(mid+1,r,(k<<1)|1,x,y,val); t[k] = t[k<<1] + t[(k<<1)|1]; } LL query(int l,int r,int k,int x,int y) { //printf("%d %d %d\n",l,r,t[k]); if (x <= l && r <= y) { return t[k]; } if (lazy[k]) down(l,r,k); int mid = (l + r) >> 1;LL s1 = 0,s2 = 0; if (x <= mid) s1 = query(l,mid,k<<1,x,y); if (y > mid) s2 = query(mid+1,r,(k<<1)|1,x,y); return s1+s2; } int main() { int n,m; int l,r;LL v; while (scanf("%d",&n) != EOF) { build(1,n,1); scanf("%d",&m); for (int i = 1;i <= m; i++) { scanf("%d%d%lld",&l,&r,&v); updata(1,n,1,l,r,v); printf("%lld\n",query(1,n,1,l,r)); } } return 0; }
【UESTC1060】秋实大哥与快餐店
给出一些数a[1..n],有两种操作,再给出一个数a[i],或是对一个数k,求出给定的数中与k异或值最大的数。字典树的应用,可以将给出的数a[i]转换为二进制串后,从高位到低位进行建树(为保证位数相同需要补零)。对于给定的数k,在走到第i层节点时,尽量寻找与k第i位值不同的节点,这样找到的数就是与k异或值最大的数。
#include<iostream> #include<cstdio> #include<map> #include<set> #include<vector> #include<stack> #include<queue> #include<string> #include<cstring> #include<sstream> #include<algorithm> #include<cmath> #define INF 0x3f3f3f3f #define eps 1e-8 #define pi acos(-1.0) using namespace std; struct node{ int k,next[2]; }; vector<node> a; int cnt,root; int newnode() { node n; n.k = n.next[0] = n.next[1] = 0; a.push_back(n); //cout << cnt << endl; return a.size()-1; } void ins(int x) { int p = root,k; for (int i = 21;i >= 0; i--) { k = x&(1 << i)?1:0; //cout << k << endl; if (!a[p].next[k]) { int sav = newnode(); a[p].next[k] = sav; //cout << a[p].next[k] << endl; } //printf("%d %d %d\n",p,a[p].next[0],a[p].next[1]); p = a[p].next[k]; } } int query(int x) { int p = root,k,ans = 0; for (int i = 21;i >= 0; i--) { k = x&(1 << i)?0:1; if (!a[p].next[k]) k = 1 - k; if (k) ans += (1 << i); p = a[p].next[k]; } return ans; } int main() { int n,m,x,y; while (scanf("%d",&n) != EOF) { a.clear();cnt = 0; root = newnode(); for (int i = 1;i <= n; i++) { scanf("%d",&x); ins(x); } scanf("%d",&m); while (m--) { scanf("%d%d",&x,&y); if (!x) ins(y); else printf("%d\n",query(y)); } } return 0; }
【UESTC1061】秋实大哥与战争
问题的实质在于寻找士兵k左右最近的死亡士兵,设左边最近的死亡士兵为l,右边的为r,则答案为r-l-1,也就是说,我们要完成(1)添加一个数(2)删除一个数(3)寻找与给定的数k最近的两个数,而集合set恰好能满足我们的需要(set是有序的,可以使用lower_bound进行查询),我们使用两个集合,一个集合存储正的坐标用来寻找l,另一个存储负坐标用于寻找r,在最开始需要把n+1和0添加到集合中。
#include<iostream> #include<cstdio> #include<map> #include<set> #include<vector> #include<stack> #include<queue> #include<string> #include<cstring> #include<sstream> #include<algorithm> #include<cmath> #define INF 0x3f3f3f3f #define eps 1e-8 #define pi acos(-1.0) using namespace std; set<int> Sinc,Sdec; int main() { int n,m; int act,k; while (scanf("%d%d",&n,&m) != EOF) { Sinc.clear();Sdec.clear(); Sinc.insert(n+1); Sdec.insert(0); while (m--) { scanf("%d%d",&act,&k); if (act == 0) {Sinc.insert(k);Sdec.insert(-k);} if (act == 1) {Sinc.erase(k);Sdec.erase(-k);} if (act == 2) { set<int>::iterator st = Sinc.lower_bound(k); set<int>::iterator ed = Sdec.lower_bound(-k); if (*st == k) puts("0"); else printf("%d\n",(*st)+(*ed)-1); } } } return 0; }
【UESTC1063】秋实大哥与妹纸
寻找a1...an的中位数,即第k大数问题,用堆或快排的折半查找均可。
#include<iostream> #include<cstdio> #include<map> #include<set> #include<vector> #include<stack> #include<queue> #include<string> #include<cstring> #include<sstream> #include<algorithm> #include<cmath> #define INF 0x3f3f3f3f #define eps 1e-8 #define pi acos(-1.0) using namespace std; typedef unsigned int LL; LL a[250000],m; void add(int k) { LL val = a[k]; while (k > 1) { if (val > a[k >> 1]) { a[k] = a[k >> 1]; k >>= 1; } else break; } a[k] = val; } void del() { LL val = a[m--],k = 1; while (k < m) { if ((k << 1) > m) break; if ((k << 1) == m || a[(k << 1)|1] < a[k << 1]) { if (a[(k << 1)] > val) { a[k] = a[(k << 1)]; k = (k << 1); } else break; } else { if (a[(k << 1)|1] > val) { a[k] = a[(k << 1)|1]; k = (k << 1)|1; } else break; } } a[k] = val; } int main() { int n,k,mid; scanf("%d",&n);mid = n/2 + 1; for (int i = 1;i <= mid; i++) { scanf("%ud",&a[++m]); add(m); } for (int i = mid+1;i <= n; i++) { scanf("%ud",&a[++m]); add(mid+1); del(); } //for (int i = 1;i <= mid; i++) cout << a[i] << " ";cout << endl; if ((n&1) == 0) { int k = a[1];del(); printf("%.1lf\n",(k+a[1])/2.0); } else { printf("%.1lf\n",(double)a[1]); } return 0; }
寻找最大的连续矩形,明显的,最大矩形的高度一定与某个给定的矩形高度相等,所以对于每一个给定的矩形,以ai为高度的连续矩形可以向左右延伸,直到找到aj<ai为止。使用单调栈存储递增的矩形序列,入栈时确定了连续矩形的左边界,出栈时确定了连续矩形的右边界。
#include<iostream> #include<cstdio> #include<map> #include<set> #include<vector> #include<stack> #include<queue> #include<string> #include<cstring> #include<sstream> #include<algorithm> #include<cmath> #define INF 0x3f3f3f3f #define eps 1e-8 #define pi acos(-1.0) using namespace std; struct mat{ int h,w; }; mat Q[200100]; int main() { int n; scanf("%d",&n); mat m; int p = 0,ans = 0; for (int i = 1;i <= n; i++) { scanf("%d%d",&m.w,&m.h); int sumw = 0; while (p > 0 && m.h <= Q[p].h) { ans = max(ans,Q[p].h*(Q[p].w+sumw)); sumw += Q[p--].w; } m.w += sumw; Q[++p] = m; } int sumw = 0; while (p > 0) { ans = max(ans,Q[p].h*(Q[p].w + sumw)); sumw += Q[p--].w; } printf("%d\n",ans); return 0; }
有权并查集,添加一个dis[i]数组表示i节点到f[i]节点的距离,在find中更新f[i]和dis[i]数组,注意读入有坑……最后一行没换行。
#include<iostream> #include<cstdio> #include<map> #include<set> #include<vector> #include<stack> #include<queue> #include<string> #include<cstring> #include<sstream> #include<algorithm> #include<cmath> #define INF 0x3f3f3f3f #define eps 1e-8 #define pi acos(-1.0) using namespace std; const int maxn = 1e5 + 100; int f[maxn],dis[maxn]; stack<int> S; int find(int k) { while (f[k] != k) { S.push(k); k = f[k]; } int sum = 0; while (!S.empty()) { int p = S.top();S.pop(); sum += dis[p];dis[p] = sum; f[p] = k; } return k; } int main() { int n,x,y,fy;char ch; scanf("%d",&n); for (int i = 1;i <= n; i++) {f[i] = i;dis[i] = 0;} while (scanf(" %c",&ch) != EOF && ch != 'O') { if (ch == 'E') { scanf("%d",&x); find(x); printf("%d\n",dis[x]); } if (ch == 'I') { scanf("%d%d",&x,&y); x = find(x);fy = find(y); if (x != fy) {f[x] = y;dis[x] = abs(x-y)%1000;} } } //for (int i = 1;i <= n; i++) printf("%d %d\n",f[i],dis[i]); return 0; }
【UESTC1073】秋实大哥与线段树
没什么可说的……
#include<iostream> #include<cstdio> #include<map> #include<set> #include<vector> #include<stack> #include<queue> #include<string> #include<cstring> #include<sstream> #include<algorithm> #include<cmath> #define INF 0x3f3f3f3f #define eps 1e-8 #define pi acos(-1.0) using namespace std; typedef long long LL; const int maxn = 1e5 + 100; int n,m; LL a[maxn],t[maxn]; void add(int k,int val) { for (int i = k;i <= n; i += i&(-i)) t[i] += val; } LL query(int k) { LL ans = 0; for (int i = k;i > 0; i -= i&(-i)) ans += t[i]; return ans; } int main() { int l,r,k; scanf("%d",&n); for (int i = 1;i <= n; i++) { scanf("%d",&a[i]); add(i,a[i]); } scanf("%d",&m); for (int i = 1;i <= m; i++) { scanf("%d%d%d",&k,&l,&r); if (k == 1) {add(l,r-a[l]);a[l] = r;} if (k == 2) printf("%lld\n",query(r)-query(l-1)); } return 0; }
【UESTC1074】秋实大哥搞算数
四则表达式计算,栈的基本应用,正统做法是为运算符规定优先级……这里因为没有括号就偷懒了。减法运算转化为加上负数可以使运算简便一些。
#include<iostream> #include<cstdio> #include<map> #include<set> #include<vector> #include<stack> #include<queue> #include<string> #include<cstring> #include<sstream> #include<algorithm> #include<cmath> #define INF 0x3f3f3f3f #define eps 1e-8 #define pi acos(-1.0) using namespace std; typedef long long LL; string st; stack<LL> num; stack<char> op; void fig() { LL k2 = num.top();num.pop(); LL k1 = num.top();num.pop(); char act = op.top();op.pop(); //cout << k1 << act << k2 << endl; if (act == '+') k1 += k2; if (act == '-') k1 -= k2; if (act == '*') k1 *= k2; if (act == '/') k1 /= k2; num.push(k1); } int main() { int t; scanf("%d",&t); while (t--) { num.push(0); LL k = 0;int sgn = 1; cin >> st; for (int i = 0;i < st.size(); i++) { if (st[i] >= '0' && st[i] <= '9') k = k*10 + (st[i] - '0')*sgn; else { //cout << k << endl; num.push(k);k = 0; while (!op.empty() && (op.top() == '*' || op.top() == '/')) fig(); if (st[i] == '-') { st[i] = '+'; sgn = -1; } else sgn = 1; op.push(st[i]); } } num.push(k);k = 0; while (!op.empty()) fig(); if (!num.empty()) { printf("%lld\n",num.top()); while (!num.empty()) num.pop(); } } return 0; }