2019-2020 ICPC Asia Hong Kong Regional Contest(补题中)
加粗:赛时AC
普通:赛后AC
B. Binary Tree
轮到某个人时他拿去的数目一定是奇数,判断n奇偶即可。
C. Constructing Ranches(点分治)
参考的是这篇文章
多边形的各边和大于最大边的两倍。
考察树上所有路径的问题点分治是一种常见做法,用点分治然后维护点到根节点的最大值和路径权值,将计算的数字按照最大值进行排序,然后用一个数据结构维护即可。
#include<iostream> #include<cstdio> #include<string> #include<cmath> #include<algorithm> #include<cstring> #include<queue> #include<algorithm> #include<queue> #define N 200050 #define ll long long using namespace std; ll T,n; ll a[N],b[N]; ll heads[N],tot,rt,k; ll vis[N]; ll sz[N],sum; ll ans; ll dp[N]; ll C[N]; pair<ll, int> p[N], val[N]; //��� inline void read(ll &p) { p=0; ll f=1;char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9') p=p*10+(ch-'0'),ch=getchar(); p*=f; } struct edge{ int v,nxt; }ed[N<<1]; inline int lb(int x) { return x & (-x); } inline void addin(int u,int v) { ed[++tot].v=v; ed[tot].nxt=heads[u]; heads[u]=tot; } inline void getroot(int u,int fa) { sz[u]=1; dp[u]=0; for(int i=heads[u];i!=0;i=ed[i].nxt) { int v=ed[i].v; if(vis[v]||v==fa) continue; getroot(v,u); sz[u]+=sz[v]; dp[u]=max(dp[u],sz[v]); } dp[u]=max(dp[u],sum-sz[u]); if(dp[u]<dp[rt]) rt=u; } ll cnt; inline void add_dfs(int u,int mx,ll w,int fa) { cnt++; p[cnt] = {w, cnt}; val[cnt].first=mx; for(int i=heads[u];i!=0;i=ed[i].nxt) { int v=ed[i].v; if(vis[v]||v==fa) continue; add_dfs(v,max(1ll*mx,a[v]),w+a[v],u); } } inline void addv(int x,int v) { while(x<=cnt) { C[x]+=v; x+=lb(x); } } inline int query(int x) { int sum=0; while(x) { sum+=C[x]; x-=lb(x); } return sum; } inline ll cal(int u,int w) { cnt=0; add_dfs(u,max(1ll*w,a[u]),w+a[u],0); int hd = (w ? w : val[1].first); sort(p+1,p+1+cnt); for(int i=1;i<=cnt;i++) val[p[i].second].second=i; for(int i=0;i<=cnt;i++) C[i]=0; ll res=0; sort(val+1,val+1+cnt); for(int i=1;i<=cnt;i++) { ll sw=2LL * val[i].first+hd - p[val[i].second].first; int l=1,r=cnt,as=0; while(l<=r) { int mid=(l+r)>>1; if(p[mid].first<=sw) { l=mid+1; as=mid; } else r=mid-1; } res+=i-1-query(as); addv(val[i].second,1); } return res; } inline void go(int u) { vis[u]=1; ans+=cal(u,0); for(int i=heads[u];i!=0;i=ed[i].nxt) { int v=ed[i].v; if(vis[v]) continue; ans-=cal(v,a[u]); rt=0; dp[0]=sum=sz[v]; getroot(v,0); go(rt); } } inline void init() { for(int i=0;i<=n;i++) heads[i]=0,vis[i]=0; tot=0;ans=0; } inline void solve() { read(n); init(); for(int i=1;i<=n;i++) read(a[i]); for(int i=1;i<n;i++) { ll x,y; read(x);read(y); addin(x,y); addin(y,x); } rt=0; dp[0]=sum=n; getroot(1,0); go(rt); printf("%lld\n",ans); } int main() { read(T); while(T--) { solve(); } return 0; }
D. Defining Labels
壕哥过的,本质还是进制转换,只不过答案输出时要进行偏移。
E. Erasing Numbers
数据n为5e3,可以用n2完成。那么我们枚举每个数字,由于数字各不相同,所以我们把大于当前数字的数标记为1,小于的标记为1。根据题意,当+1和-1的数字相等时,一定可以留下该数字,那么我们就从左到右扫描,记录+1和-1的个数,然后记录两个数字的可删除范围,判断两者是否可能相等即可,这个过程最好用类似于栈的方式去维护,有+1和-1相邻就直接消去,因为有可能出现原本没有连续的三个数字,删去之后产生的情况。
听说这题的n可以给的更大,然后用线段树维护,逮捕
#include<iostream> #include<cstdio> #include<string> #include<cmath> #include<algorithm> #include<cstring> #include<queue> #include<algorithm> #include<queue> #define N 1000010 #define ll long long using namespace std; ll T,n; ll a[N],b[N],ans[N]; //快读 inline void read(ll &p) { p=0; ll f=1;char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9') p=p*10+(ch-'0'),ch=getchar(); p*=f; } int main() { read(T); while(T--) { read(n); for(int i=1;i<=n;i++) read(a[i]); for(int i=1;i<=n;i++) { int totp1=0,totp2=0,tot11=0,tot22=0,tot1=0,tot2=0; int pos=0; for(int j=1;j<=n;j++) { if(a[j]>i) { tot11++; tot2= tot2>0 ? tot2-1 : 0; b[j]=1; tot1++; if(tot1>=3) tot1-=2,totp1+=2; } if(a[j]<i) { tot22++; tot1= tot1>0 ? tot1-1 : 0; b[j]=-1; tot2++; if(tot2>=3) tot2-=2,totp2+=2; } if(a[j]==i) { pos=j; b[j]=0; tot2=0; tot1=0; } } if(tot22>=tot11) { if(tot22-totp2<=tot11) ans[pos]=1; else ans[pos]=0; } else { if(tot11-totp1<=tot22) ans[pos]=1; else ans[pos]=0; } } for(int i=1;i<=n;i++) printf("%d",ans[i]); printf("\n"); } return 0; } /* 11 11 6 3 8 4 9 2 7 10 1 5 */
G. Game Design
杰哥壕哥说他们做过,还说是水题,我就没管了,可恶啊,队友什么时候背着我偷偷优秀了。
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #include <array> #include <vector> #include <map> #include <unordered_map> #include <set> #include <unordered_set> #include <stack> #include <queue> #include <bitset> #include <algorithm> #include <numeric> #include <functional> #include <cmath> using namespace std; typedef long long ll; #define finc(i,a,b) for(int i=(int)(a);i<(int)(b);i++) #define fdec(i,a,b) for(int i=(int)(b);i-->(int)(a);) #define reset(a,...) a=decltype(a)(__VA_ARGS__) #define endl '\n' #define endb ' ' #define read(a,str) scanf("%"#str,&a) #define print(a,str) printf("%"#str,a) vector<int> ans; vector<int> edge; void f(int val, int now, int cnt) { if (cnt & 1) ans.push_back(val); else ans.push_back(val + 1); cnt >>= 1; if (cnt == 0) return; ans.push_back(1), ans.push_back(1); edge.push_back(now), edge.push_back(now + 1); edge.push_back(now); f(val - 1, now + 3, cnt); } void ac() { int k; cin >> k; if (k == 1) { cout << 2 << endl; cout << 1 << endl; cout << 101 << endb << 100 << endl; return; } f(100, 1, k); cout << ans.size() << endl; for (auto& I : edge) cout << I << endb; cout << endl; for (auto& I : ans) cout << I << endb; cout << endl; } int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int test = 1; //cin >> test; while (test--) ac(); return 0; }
I. Incoming Asteroids
这题最后还剩一个小时的时候出的解法,但是打WA了,答案异或的时候带上了k,结果RE了,最后是杰哥补完的题。
不过这题能出解法还算不错了。
主要切入点是每个人最多有1~3个相机,如果每来一次事件2都找一遍哪个人完成了是n2的做法。
如果一个人只有一个相机,那么我们每个天文台使用一个堆就能够优化做法,但是有的人有多个相机,事情就变的棘手起来了。
我们考虑给每个相机分配任务,假如有2个相机就每个分配 $ \frac y 2 $ 的任务,3个就分配$ \frac y 3 $的任务(向上取整),然后将任务放进对应堆里面,很显然如果每个相机都没完成自己的任务,那么这个人一定没有完成,如果有一个相机完成了,我们看3个相机录制的总时间是否符合要求(时间标记在天文台上,后面来的任务标记为原任务时间+天文台时间标记),如果符合标记完成输出,然后再遇到这个人的任务时就不做,否则的话,就用y-掉总的已录制时间,然后用这个值重新分配任务,行分配的任务一定比原本还在堆里的任务先完成,这样就将时间复杂度降为了 $ O(mlogylogn) $.
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #include <array> #include <vector> #include <map> #include <unordered_map> #include <set> #include <unordered_set> #include <stack> #include <queue> #include <bitset> #include <algorithm> #include <numeric> #include <functional> #include <cmath> using namespace std; typedef long long ll; #define finc(i,a,b) for(int i=(int)(a);i<(int)(b);i++) #define fdec(i,a,b) for(int i=(int)(b);i-->(int)(a);) #define reset(a,...) a=decltype(a)(__VA_ARGS__) #define endl '\n' #define endb ' ' #define read(a,str) scanf("%"#str,&a) #define print(a,str) printf("%"#str,a) struct Node { ll val; int id; Node() :val(0), id(0) {} Node(ll _val, int _id) :val(_val), id(_id) {} bool operator>(const Node& x)const { return val > x.val; } }; struct Data :public vector<int> { ll val; int size; Data() :val(0), size(0) {} Data(ll _val, int _size, vector<int>& _vec) :val(_val), size(_size), vector<int>(_vec) {} }; int n, m; vector<bool> visited; vector<Data> query; vector<ll> top; vector<priority_queue<Node, vector<Node>, greater<Node>>> buc; void ac() { read(n, d), read(m, d); reset(buc, n + 1), reset(top, n + 1), reset(visited, m + 1), reset(query, 1); int ans = 0; while (m--) { int flag; read(flag, d); if (flag == 1) { int y, k; read(y, d), read(k, d); y ^= ans; vector<int> vec(k); finc(i, 0, k) read(vec[i], d), vec[i] ^= ans; ll sum = 0; finc(i, 0, k) buc[vec[i]].push({ top[vec[i]] + (y + k - 1) / k,(int)query.size() }), sum += top[vec[i]]; query.push_back({ sum + y,k,vec }); continue; } int x, y; read(x, d), read(y, d), x ^= ans, y ^= ans; top[x] += y; ans = 0; vector<int> out; while (!buc[x].empty()) { auto now = buc[x].top(); if (visited[now.id]) { buc[x].pop(); continue; } if (now.val > top[x]) break; buc[x].pop(); ll cnt = 0; for (auto& I : query[now.id]) cnt += top[I]; if (cnt >= query[now.id].val) ans++, visited[now.id] = 1, out.push_back(now.id); else { now.val = (query[now.id].val - cnt + query[now.id].size - 1) / query[now.id].size; for (auto& I : query[now.id]) buc[I].push({ now.val + top[I], now.id }); } } if (ans == 0) cout << ans << endl; else { sort(out.begin(), out.end()); cout << ans << endb; finc(i, 0, out.size() - 1) cout << out[i] << endb; cout << out.back() << endl; } } } int main() { //ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int test = 1; //cin >> test; while (test--) ac(); return 0; }
J. Junior Mathematician
壕哥写得,据说是数位DP,队友会了就相当于我会了(
#include <iostream> #include <cstdio> using namespace std; const int N=5e3+10; const int mod=1e9+7; #define ll long long char s1[N],s2[N]; ll dp[N][60][60]; int a1[N],a2[N],m,g[N],l1,l2; int dfs(int i,int j,int k,int p,int kind) { if (!i) { if (k==0) return 1; else return 0; } if (!p&&(dp[i][j][k]!=-1)) return dp[i][j][k]; int maxx,h; if (p) { if (kind==0) maxx=a1[i]; else maxx=a2[i]; } else maxx=9; ll now=0; for (h=0; h<=maxx; h++) { int nowk=(k+j*h%m-h*g[i]%m+m)%m; int nowp,hh; if (kind==0) hh=a1[i]; else hh=a2[i]; if (p&&h==hh) nowp=1; else nowp=0; now=(now+dfs(i-1,(j+h)%m,nowk,nowp,kind))%mod; } if(!p) dp[i][j][k]=now; return now; } void clean() { int i,j,k; g[1]=1; for (i=2; i<=l2; i++) g[i]=g[i-1]*10%m; for (i=1; i<=l2; i++) for (j=0; j<=m; j++) for (k=0; k<=m; k++) dp[i][j][k]=-1; } int main() { int t; scanf("%d",&t); while (t--) { getchar(); scanf("%s%s",s1,s2); scanf("%d",&m); int i,j,k; for (l1=0; s1[l1]; l1++) {} for (l2=0; s2[l2]; l2++) {} for (i=0; i<l1; i++) a1[l1-i]=s1[i]-'0'; for (i=1; i<=l1; i++) if (a1[i]>0) { a1[i]--; break; } else a1[i]=9; for (i=0; i<l2; i++) a2[l2-i]=s2[i]-'0'; clean(); int ans1=dfs(l1,0,0,1,0); int ans2=dfs(l2,0,0,1,1); printf("%d\n",(ans2-ans1+mod)%mod); } return 0; }