《2021CCPC桂林》
只做了几个题。
B. A Plus B Problem:
一开始做的时候思路有点乱,后面理清楚了,就是说不用10进制来维护,用没有进位之后的数来维护,这样每次操作之后就是加1减1的操作了。
// Author: levil #includeusing namespace std; typedef long long LL; typedef long double ld; typedef pair<int,int> pii; const int N = 2e6 + 5; const int M = 1e6 + 5; const LL Mod = 998244353; #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; inline long long ADD(long long x,long long y) { if(x + y < 0) return ((x + y) % Mod + Mod) % Mod; return (x + y) % Mod; } inline long long MUL(long long x,long long y) { if(x * y < 0) return ((x * y) % Mod + Mod) % Mod; return x * y % Mod; } inline long long DEC(long long x,long long y) { if(x - y < 0) return (x - y + Mod) % Mod; return (x - y) % Mod; } int a[5][N],cd[N]; struct Node{int L,r,mi,mx,tag;}node[N << 2]; void Pushup(int idx) { node[idx].mx = max(node[idx << 1].mx,node[idx << 1 | 1].mx); node[idx].mi = min(node[idx << 1].mi,node[idx << 1 | 1].mi); } void Pushdown(int idx) { if(node[idx].tag != 0) { int tag = node[idx].tag; node[idx << 1].mx += tag,node[idx << 1].mi += tag,node[idx << 1].tag += tag; node[idx << 1 | 1].mx += tag,node[idx << 1 | 1].mi += tag,node[idx << 1 | 1].tag += tag; node[idx].tag = 0; } } void build(int L,int r,int idx) { node[idx].L = L,node[idx].r = r,node[idx].tag = 0; if(L == r) { node[idx].mi = node[idx].mx = cd[L]; return ; } int mid = (L + r) >> 1; build(L,mid,idx << 1); build(mid + 1,r,idx << 1 | 1); Pushup(idx); } void update(int L,int r,int val,int idx) { if(node[idx].L > node[idx].r || L > r) return ; if(node[idx].L >= L && node[idx].r <= r) { node[idx].mi += val; node[idx].mx += val; node[idx].tag += val; return ; } Pushdown(idx); int mid = (node[idx].L + node[idx].r) >> 1; if(mid >= L) update(L,r,val,idx << 1); if(mid < r) update(L,r,val,idx << 1 | 1); Pushup(idx); } void update2(int x,int val,int idx) { if(node[idx].L == node[idx].r) { node[idx].mi = node[idx].mx = val; return ; } Pushdown(idx); int mid = (node[idx].L + node[idx].r) >> 1; if(mid >= x) update2(x,val,idx << 1); else update2(x,val,idx << 1 | 1); Pushup(idx); } int query(int x,int idx) { if(node[idx].L == node[idx].r) return node[idx].mi; Pushdown(idx); int mid = (node[idx].L + node[idx].r) >> 1; if(mid >= x) return query(x,idx << 1); else return query(x,idx << 1 | 1); } int query_pos(int L,int r,int id,int idx) {//id = 1 != 10,id = 2,!= 9 if(L > r) return -1; if(node[idx].L == node[idx].r) return node[idx].L; Pushdown(idx); int mid = (node[idx].L + node[idx].r) >> 1,val = 9; if(id == 1) val = 10; if(mid < r && (node[idx << 1 | 1].mx != val || node[idx << 1 | 1].mi != val)) { int ans = query_pos(L,r,id,idx << 1 | 1); if(ans != -1) return ans; else if(mid >= L && (node[idx << 1].mx != val || node[idx << 1].mi != val)) return query_pos(L,r,id,idx << 1); return -1; } else if(mid >= L && (node[idx << 1].mx != val || node[idx << 1].mi != val)) return query_pos(L,r,id,idx << 1); return -1; } void solve() { int n,q;scanf("%d %d",&n,&q); string s;cin >> s; for(int i = 1;i <= n;++i) a[1][i] = s[i - 1] - '0'; cin >> s; for(int i = 1;i <= n;++i) a[2][i] = s[i - 1] - '0'; int t = 0; for(int i = n;i >= 1;--i) { int f = (a[1][i] + a[2][i] + t) / 10; cd[i] = a[1][i] + a[2][i] + t; t = f; } build(1,n,1); while(q--) { int r,c,d;scanf("%d %d %d",&r,&c,&d); int ch = 0,id; if(r == 1) id = 1; else id = 2; if(a[id][c] != d) ch++; int dis = a[id][c] - d; a[id][c] = d; int tmp = query(c,1); int ed = tmp - dis; if(tmp != ed) ch++; update2(c,ed,1); if(ed < 10 && tmp >= 10){ int pos = query_pos(1,c - 1,1,1); if(pos == -1) pos = 1,ch += c - 1; else ch += c - pos; update(pos,c - 1,-1,1); } else if(ed >= 10 && tmp < 10) { int pos = query_pos(1,c - 1,2,1); if(pos == -1) pos = 1,ch += c - 1; else ch += c - pos; update(pos,c - 1,1,1); } printf("%d %d\n",ed % 10,ch); } } int main() { // int ca;scanf("%d",&ca); // while(ca--) { solve(); //} //system("pause"); return 0; } /* 5 5 05617 14382 1 3 2 2 5 4 5 5 26321 47764 2 5 9 2 4 7 1 4 2 1 3 2 1 4 3 5 5 99990 00000 2 4 1 2 5 1 2 5 1 */
E:Buy and Delete
这题相对要简单一些,只要能想到了,主要是判断能不能买到环的情况,如果能买到一个,和买到多个一样都是2轮。也就是说找到一个环的权值最小。
dij找一下最小权值环就行了。
D:Assumption is All You Need 银牌题?
这感觉也就是个2000多分左右的构造,不是很难,大概半小时推出了解法。
考虑对于一个小的数,一次逆序对操作之后,它只会向前走。
我们拟定现在比它小的数都无法动了。那么如果目标位置在它后面,那么如果不靠比它小的肯定无解。
现在我们去动比它小的,很显然,这个移动规律有传递性,如果我们利用比它小的完成了,那么比它小的肯定在更前面了。
所以我们考虑从小到大去对位,那么对于当前i,如果需要借用小的,那么肯定无解。
经过上面操作很显然,我们每次去动都是向左移动,那么每次对位我们都去找一个可动区间里的最小的替换它是最优的。
最优的这个很容易证明,因为比他大的都可以移动到这个位置,所以找最小的。
// Author: levil #includeusing namespace std; typedef long long LL; typedef long double ld; typedef pair<int,int> pii; const int N = 2e6 + 5; const int M = 1e6 + 5; const LL Mod = 998244353; #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; inline long long ADD(long long x,long long y) { if(x + y < 0) return ((x + y) % Mod + Mod) % Mod; return (x + y) % Mod; } inline long long MUL(long long x,long long y) { if(x * y < 0) return ((x * y) % Mod + Mod) % Mod; return x * y % Mod; } inline long long DEC(long long x,long long y) { if(x - y < 0) return (x - y + Mod) % Mod; return (x - y) % Mod; } int a[2025],b[2025],pos1[2025],pos2[2025]; vector ans; int Find(int L,int r,int k) { int mi = INF; for(int i = L;i <= r;++i) { if(a[i] > k && a[i] < mi) mi = a[i]; } return mi; } void change(int x,int y) { int xx = pos1[x],yy = pos1[y]; pos1[y] = xx,pos1[x] = yy; swap(a[xx],a[yy]); ans.push_back(pii{min(xx,yy),max(xx,yy)}); } void solve() { ans.clear(); int n;scanf("%d",&n); for(int i = 1;i <= n;++i) scanf("%d",&a[i]),pos1[a[i]] = i; for(int i = 1;i <= n;++i) scanf("%d",&b[i]),pos2[b[i]] = i; bool flag = 0; for(int i = 1;i <= n;++i) { if(pos2[i] > pos1[i]) {flag = 1;break;} while(1) { int tmp = Find(pos2[i],pos1[i] - 1,i); if(tmp == INF) break; change(i,tmp); } if(pos1[i] != pos2[i]) change(i,a[pos2[i]]); } if(flag) printf("-1\n"); else { printf("%d\n",ans.size()); for(auto v : ans) printf("%d %d\n",v.first,v.second); } } int main() { int ca;scanf("%d",&ca); while(ca--) { solve(); } //system("pause"); return 0; }