2018 icpc徐州網路賽
阿新 • • 發佈:2018-12-09
F I簽到題,直接模擬即可。
A:
當第一個有2^k種選擇時,由於每一個數相對於的進行XNOR為0的數是一定的,所以第二個數有2^k-1種選擇,因而對於第n個人有2^k-2種選擇,然而,若第n-1個人與第一個人選擇相同時,將第一個數與最後一個數看做同一個數,那麼序列就縮短為n-2的長度,因而如此遞推下去即可求出答案。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define ll long long #define MOD 1000000007 #define N 1000005 ll a[N]; ll quickpow(ll a, ll b) { ll ans = 1; while(b) { if(b%2) ans = ans*a%MOD; a = a*a%MOD; b /= 2; } return ans; } ll solve(int n, int k) { if(n == 1) return a[k]; else if(n == 2) return a[k] * (a[k] - 1) % MOD; else { return (a[k] * quickpow(a[k]-1, n-2) % MOD * max(a[k]-2, 0ll) % MOD + solve(n-2, k) % MOD) % MOD; } } int main() { a[0] = 1; for(int i = 1; i < N; i++) a[i] = a[i-1] * 2 % MOD; int t; scanf("%d", &t); while(t--) { int n, k; scanf("%d%d", &n, &k); printf("%lld\n", solve(n, k)); } return 0; } /* 2 3 1 4 2 */
H:
1.線段樹做法:
設定兩個陣列,一個sum用來存區間和,另一個sum1用來存(n-l+1)*a[l],這樣每次查詢只需要用在 b到c的區間內的 sum1的區間和減去sum的區間和*(n-c)。(瘋狂TLE的一道線段樹裸題)
#include <cstdio> #include <cstring> using namespace std; #define N 100100 long long sum[N<<2], sum1[N<<2], a[N]; int n; void Build(int i, int l, int r) { if(l == r) { sum[i] = a[l]; sum1[i] = (n - l + 1) * a[l]; return; } int mid = (l + r) >> 1; Build(i<<1, l, mid); Build(i<<1|1, mid+1, r); sum[i] = sum[i<<1] + sum[i<<1|1]; sum1[i] = sum1[i<<1] + sum1[i<<1|1]; } long long Query(int i, int l, int r, int L, int R, long long s[]) { if(l <= L && r >= R) { return s[i]; } int mid = (L + R) >> 1; if(r <= mid) return Query(i<<1, l, r, L, mid, s); else if(l > mid) return Query(i<<1|1, l, r, mid+1, R, s); else return Query(i<<1, l, r, L, mid, s) + Query(i<<1|1, l, r, mid+1, R, s); } void Change(int t, int L, int R, long long c, int i) { if(t == L && t == R) { sum[i] = c; sum1[i] = c * (n - t + 1); return; } int mid = (L + R) >> 1; if(t <= mid) Change(t, L, mid, c, i<<1); else Change(t, mid+1, R, c, i<<1|1); sum[i] = sum[i<<1] + sum[i<<1|1]; sum1[i] = sum1[i<<1] + sum1[i<<1|1]; } int main() { int q; scanf("%d%d", &n, &q); for(int i = 1; i <= n; i++) scanf("%lld", &a[i]); Build(1, 1, n); while(q--) { long long a, b, c; scanf("%lld%lld%lld", &a, &b, &c); if(a == 1) { long long ans = 0; ans += Query(1, b, c, 1, n, sum1); ans -= (n - c) * Query(1, b, c, 1, n, sum); printf("%lld\n", ans); } else { Change(b, 1, n, c, 1); } } return 0; }
2.樹狀陣列做法:
用樹狀陣列字首和求:
#include <cstdio> #include <cstring> using namespace std; #define N 100005 long long a[N], b[N]; int n, q; int lowbit(int x) { return x & (-x); } long long Query(long long s[], int x) { long long ans = 0; for(int i = x; i > 0; i -= lowbit(i)) { ans += s[i]; } return ans; } void Change(long long s[], int x, long long c) { for(int i = x; i <= n; i += lowbit(i)) { s[i] += c; } } int main() { scanf("%d%d", &n, &q); for(int i = 1; i <= n; i++) { long long t; scanf("%lld", &t); Change(a, i, t); Change(b, i, i*t); } while(q--) { long long s, l, r; scanf("%lld%lld%lld", &s, &l, &r); if(s == 1) { long long ans = (r + 1) * (Query(a, r) - Query(a, l-1)) - (Query(b, r) - Query(b, l-1)); printf("%lld\n", ans); } else { long long x = Query(a, l) - Query(a, l-1); Change(a, l, r-x); Change(b, l, l*r-l*x); } } return 0; }
G:手動模擬一遍,然後從後往前計算波浪的長度,模擬的時候就知道怎麼計算長度了...但是一開始用的是陣列加sort然後瘋狂TLE,然後查了之後才知道...用了這麼久的set居然不知道set會自動排序!!!
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
#define N 50005
int n;
long long X[N], Y[N];
long long solve(long long a[])
{
set <int> Q;
set <int> :: iterator it; //這個時候真的想說set真的很有用...
long long ans = 0;
for(int i = n - 1; i >= 0; i--)
{
it = Q.lower_bound(a[i]);
if(it == Q.begin()) ans += a[i];
else
{
it--;
ans += a[i] - *it;
}
Q.insert(a[i]);
}
return ans;
}
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
scanf("%lld%lld", &X[i], &Y[i]);
}
printf("%lld\n", solve(X) + solve(Y));
return 0;
}
J:
求一個最大生成樹+LCA離線演算法