【11.2校內測試】【狀壓】【矩陣字首和】【樹狀陣列逆序對(題意轉換)】
Solution
簽到水題,直接狀壓列舉所有情況算出答案即可。
Code
#include<bits/stdc++.h> #define LL long long using namespace std; inline LL read() { LL x = 0, t = 0; char ch = getchar(); while(!isdigit(ch)) t |= (ch == '-'), ch = getchar(); while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();return x * (t ? -1 : 1); } LL n; LL oi, wh, a[20], b[20], c[20], d[20]; void cal(int s) { oi = wh = 0; for(int i = 1; i <= n; i ++) { int t = (s >> (i - 1)) & 1; if(t) { wh += a[i]; oi = max(1ll * 0, oi - b[i]); } else { oi+= c[i]; wh = max(1ll * 0, wh - d[i]); } } } int main() { freopen("week.in", "r", stdin); freopen("week.out", "w", stdout); n = read(); for(int i = 1; i <= n; i ++) { a[i] = read(), b[i] = read(), c[i] = read(), d[i] = read(); } LL ans= 0; for(int s = 0; s < (1 << n); s ++) { cal(s); ans = max(ans, oi * wh); } printf("%lld", ans); return 0; }
Solution
考完看題解淚流滿面了QAQ
題中不斷強調兩點之間只有一條簡單路徑啊!!!那不就是樹嘛QAQ
而且在特定的一個矩陣中也要滿足這個性質,那麼這個矩陣中所有聯通塊相當於森林,顯然聯通塊數等於點數減邊數。
所以對每個點特定向上和向左建邊,用矩陣字首和處理就好了。
主要是統計答案時發現第一排會多出來向上連的邊不能算,第一列會多出來向左連的邊不能算,所以預處理時同時處理出每列向上連的邊數字首和,每行向左連的邊數的字首和,最後單獨加上這兩排的貢獻即可。
Code
#include<bits/stdc++.h> #define LL long long using namespace std; inline int read() { int x = 0, t = 0; char ch = getchar(); while(!isdigit(ch)) t |= (ch == '-'), ch = getchar(); while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * (t ? -1 : 1); } int n, m, q; LL G[2005][2005], B[2005][2005], h[2005][2005], l[2005][2005], b[2005][2005]; char a[2005][2005]; int main() { freopen("duty.in", "r", stdin); freopen("duty.out", "w", stdout); n = read(), m = read(), q = read(); for(int i = 1; i <= n; i ++) scanf("%s", a[i] + 1); for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) { if(a[i][j] == '1') b[i][j] = 1; l[i][j] = l[i - 1][j], h[i][j] = h[i][j - 1]; if(b[i][j]) { G[i][j] ++; if(b[i - 1][j]) B[i][j] ++, l[i][j] ++; if(b[i][j - 1]) B[i][j] ++, h[i][j] ++; } G[i][j] += G[i - 1][j] + G[i][j - 1] - G[i - 1][j - 1]; B[i][j] += B[i - 1][j] + B[i][j - 1] - B[i - 1][j - 1]; } for(int i = 1; i <= q; i ++) { int a1 = read(), b1 = read(), a2 = read(), b2 = read(); LL ans1 = G[a2][b2] - G[a2][b1 - 1] - G[a1 - 1][b2] + G[a1 - 1][b1 - 1]; LL ans2 = B[a2][b2] - B[a2][b1] - B[a1][b2] + B[a1][b1]; ans2 += h[a1][b2] - h[a1][b1] + l[a2][b1] - l[a1][b1]; printf("%lld\n", ans1 - ans2); } return 0; }
Solution
考試的時候真沒看出來是逆序對QAQ
題中給的式子簡直是騙人!!想了半天怎麼可能統計出每個點QAQ!!
所以把式子分析,實際上對於加入每條線的增加貢獻,就等於與在它之前加入並它相交的線的條數。
如上圖,兩種情況得到的貢獻實際上是一樣的,第一張圖在加入2時加了1的貢獻,在加入3的時候加了2的貢獻,第二張圖同理。
考慮線段相交的性質.顯然有這樣的結論:0 < a < b,0 < c < d,那麼在(0,a),(c,0)之間連線段,在(0,b),(d,0)之間連線段,這樣的兩條線段在第一象限一定沒有交點.
答案就是x[1],x[2]…x[n]這個序列的逆序對個數.
考慮樹狀陣列演算法,我們需要求出第i個元素和前面i-1個元素形成的逆序對個數.
整個序列由若干段等差數列組成(不超過a段)(題目描述有“x[i]互不相同”).而第i個元素前面的i-1個元素也可以分成不超過a段等差數列.在每段等差數列內大於第i個元素的元素個數可以O(1)求出,因此O(a)的時間內即可求出第i個元素和前面i-1個元素組成的逆序對數.時間複雜度O(na).
可以得到a<=10時的20分.結合演算法3可以得到60分.
x[1]=a的資料告訴我們,x[i]=i*a%mod
假設x[i]=x[i-1]+a(也就是x[i-1]+a < mod),且x[i-1]和前面所有數字形成了m個逆序對,同時,除去x[i-1]和x[i]所在的等差數列,x[i]前面的所有數字可以分成k段等差序列,那麼x[i]將和前面所有數字組成m-k個逆序對.
原因在於:每段等差序列中必然有一個數字和x[i-1]能組成逆序對,但不能和x[i]組成逆序對.那麼每段等差數列的貢獻都會減1.
因此我們可以O(1)從x[i-1]的貢獻得到x[i]的貢獻.
如果x[i] < a,不存在對應的x[i-1],我們需要直接計算它的貢獻.前面有i-1個數字,我們數出有多少個數字不產生貢獻(即小於x[i]的數字個數),即可求出有多少個數字形成了逆序對.用樹狀陣列維護小於a的所有數值,可以在O(loga)的時間內完成一次這樣的計算.小於a的數字至多有a個,所以這一部分的時間複雜度為O(aloga),空間複雜度為O(a)
總的時間複雜度為O(aloga+n)
可以得到x[1]=a的20分.
現在x[1]!=a,我們只需針對最開始的一段不完整等差數列加一些特判就可以通過本題.
---------------------
作者:KGV093
來源:CSDN
原文:https://blog.csdn.net/KGV093/article/details/78170128
Code
#include<bits/stdc++.h> using namespace std; inline int read() { int x = 0, t = 0; char ch = getchar(); while(!isdigit(ch)) t |= (ch == '-'), ch = getchar(); while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * (t ? -1 : 1); } int n, x, a, mod; int pre[100005]; int lowbit(int x) { return x & -x; } int query(int x) { int res = 0; for(int i = x; i; i -= lowbit(i)) res += pre[i]; return res; } void add(int x, int d) { for(int i = x; i <= a; i += lowbit(i)) pre[i] += d; } int main() { freopen("fly.in", "r", stdin); freopen("fly.out", "w", stdout); n = read(), x = read(), a = read(), mod = read(); int cur = x, idc = 0; long long ans = 0, rev = 0;; for(int i = 1; i <= n; i ++) { if(cur >= a) { rev -= idc; if(x > cur) ++ rev; ans += rev; } else rev = i - 1 - query(cur + 1), ans += rev, add(cur + 1, 1); cur += a; if(cur >= mod) cur -= mod, ++ idc; } printf("%lld", ans); return 0; }