J. Joy of Handcraft(並查集暴力染色)
2020 China Collegiate Programming Contest - Mianyang Site
J. Joy of Handcraft
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
Little Horse always does some handcrafts, which is full of joy. This time, he builds a circuit that can turn on and off the bulbs periodically.
There are n bulbs in the circuit, the i-th of which has a period ti and a luminance xi. Formally, the i-th bulb will be turned on from the (2kti+1)-th second to the (2kti+ti)-th second, and it will be turned off from the (2kti+ti+1)-th second to the (2kti+2ti)-th second, k=0,1,2,… When the i-th bulb is on, its luminance will be xi, otherwise its luminance will be 0.
Now, Little Horse wants to know, for each second from the first second to the m-th second, what's the maximum luminance among all the bulbs.
Input
The first line of the input contains an integer T (1≤T≤100) — the number of test cases.
The first line of each test case contains two integers n,m (1≤n,m≤105) — the number of bulbs, and the number of integers you need to output. The sum of n and the sum of m will not exceed 2×105.
Then in the next n lines, the i-th line contains two integers ti,xi (1≤ti,xi≤105) — the period and the luminance of the i-th bulb.
Output
The x-th test case begins with Case #x:, and there follow m integers. The i-th integer indicates the maximum luminance among all the bulbs in the i-th second. If no bulb is on in the i-th second, output 0.
有一堆燈,每個燈有一個亮度xi,和一個開啟週期ti,每盞燈會在第(2kti+1)秒到第(2kti+ti)秒被點亮。k=0,1,2,…
輸出m行,第 i 行一個數,表示第 i 秒亮著的所有燈中,最大的 xi 是多少。
賽後出題人給出兩種思路,第一種二分線段樹,第二種暴力染色。
我們用第二種。
先按xi從大到小排序。
並查集維護連續的被染區間的右端點。
然後對ans陣列染色。
先染的不需要被操作。
#include<stdio.h> #include<algorithm> #include<string.h> using namespace std; const int maxn = 1e5+5; int buc[maxn], tr[maxn], ans[maxn]; struct node { int t, x; }e[maxn]; int cmp(node aa, node bb) { return aa.x > bb.x; } int dfs(int k)// tr[k] 儲存右端點 { if(k == tr[k]) return tr[k] = dfs(k+1); if(tr[k]) return tr[k] = dfs(tr[k]); return k-1; } int main() { int t, cnt = 0; scanf("%d", &t); while(t--) { int n, m; scanf("%d%d", &n, &m); int maxt = 0; for(int i = 1, tin, xin; i <= n; i++) { scanf("%d%d", &tin, &xin); buc[tin] = max(buc[tin], xin); maxt = max(maxt, tin); } int cntq = 0; for(int i = 1; i <= maxt; i++) if(buc[i]) e[++cntq].t = i, e[cntq].x = buc[i], buc[i] = 0; sort(e+1, e+cntq+1, cmp); for(int i = 1; i <= cntq; i++) { int tn = e[i].t, xn = e[i].x; for(int j = 1; j <= m; j += (tn<<1)) { int ed = (j+tn-1)>m?m:(j+tn-1); for(int k = j; k <= ed; k++) if(!tr[k]) tr[k] = ed, ans[k] = xn; else { k = dfs(k);//dfs 返回末端 tr[k] = max(ed, tr[k]); if(k > ed) //tr[k] = ed, ans[k] = xn; { while(j + (tn<<2) <= k) j += (tn<<1); } }//tr[i] 記錄i所屬的連續被染區間的右端點。 } }printf("Case #%d:",++cnt); for(int i = 1; i <= m; i++) printf(" %d", ans[i]),ans[i] = tr[i] = 0; puts(""); } return 0; }
賽場上想起一道寒假做過的類似的題。
https://codeforces.com/contest/999/problem/D
#include<stdio.h> #include<string.h> typedef long long ll; int a[200005]; int b[200005]; int tr[200005][2];//記錄父節點 父節點記錄下一個非滿節點 滿節點加入父結點中 int Find(int k) { if(tr[k][0] == k||tr[k][0] == -1) return k; else return tr[k][0] = Find(tr[k][0]); } int main() { ll n, m; scanf("%lld%lld",&n,&m); memset(tr,-1,sizeof(tr)); tr[0][1] = 0; ll k = n / m; ll jsq = 0; for(int i = 1; i <= n; i++) { scanf("%d",&a[i]); int t = Find(a[i] %m); if(b[t] == k) { int y = (tr[t][1] - a[i]%m + m) % m; jsq += y; a[i] += y; t = tr[t][1]; } b[t]++; if(b[t] == k)//更新區間 { tr[t][0] = t; tr[t][1] = (t + 1)%m; int x = t; if( tr[(t+1)%m][0] != -1) { x = Find((t+1)%m); tr[t][0] = x; } if( tr[(t-1+m)%m][0] != -1) tr[Find((t-1+m)%m)][0] = x; } } printf("%lld\n",jsq); for(int i = 1; i <= n; i++) printf("%d%c",a[i], (i == n ? '\n' : ' ')); return 0; }
我是fw,事先有了思路賽場上40多分鐘沒有改完。