(CF唯一不好的地方就是时差……不过还好没去考,考的话就等着滚回Div. 2了……)
A - Quiz
裸的贪心,不过要用矩阵乘法优化或者直接推通式然后快速幂。不过本傻叉做的时候脑子一片混乱,导致WA+TLE若干次,而且还做了很久(半小时)……
#include <cstdio> const int MOD = 1000000000+9; int ans,n,m,k; int power(long long x,int k) { int res = 1; for (;k;k >>= 1,x = x*x%MOD) if (k&1) res = 1ll*res*x%MOD; return res; } int main() { #ifndef ONLINE_JUDGE freopen("A.in","r",stdin); freopen("A.out","w",stdout); #endif scanf("%d%d%d",&n,&m,&k); long long i; // for (i = 0; 1; i += k) { // if (n - i <= m) break; // if (m >= k - 1) { // m -= (k - 1); // (ans += k - 1)%=MOD; // }else { // (ans += m)%=MOD; // printf("%d ",ans); // return 0; // } // } i = 1ll*(n - m)*k; //printf("%d ",i); if (i > n) { printf("%d ",m); return 0; } ans = 1ll*(n - m)*(k - 1)%MOD; int ans1 = 0; //for (; i + k <= n; i += k) ans1 = (ans1 + k)%MOD*2%MOD; ans1 = (1ll*k*(power(2,(n - i)/k + 1) - 2 + MOD))%MOD; //printf("%d ",ans1); (ans1 += MOD)%=MOD; i = (n -i)/k * k + i; (ans1 += n - i)%=MOD; printf("%d ",(ans + ans1)%MOD); }
B - Book of Evil
一开始看错题目了……其实这题很水……只要求出每个点离它自己最远的damaged的点,最后再一个一个点枚举过去,判断过去即可。
#include <cstdio> #include <algorithm> #include <cstring> const int N = 100000 + 9; int n,m,p[N],d,son[N],ec,fa[N]; struct Edge{int link,next;}es[N*2]; struct node{int dis,v;}f[N],g[N]; inline void addedge(const int x,const int y) { es[++ec].link = y; es[ec].next = son[x]; son[x] = ec; } inline void Addedge(const int x,const int y) {addedge(x,y);addedge(y,x);} inline void update(const int u,node x) { if (x.dis == -1) return; ++x.dis; if (f[u].dis < x.dis) g[u] = f[u],f[u] = x; else if (g[u].dis < x.dis) g[u] = x; } void dfs(const int root) { static int son1[N]; memcpy(son1,son,sizeof son); for (int u = root; u;) { if (son[u]) { const int v = es[son[u]].link; son[u] = es[son[u]].next; if (v == fa[u]) continue; fa[v] = u; u = v; continue; } update(fa[u],f[u]); u = fa[u]; } for (int u = root; u;) { if (son1[u]) { const int v = es[son1[u]].link; son1[u] = es[son1[u]].next; if (v == fa[u]) continue; fa[v] = u; if (f[u].v == f[v].v) update(v,g[u]); else update(v,f[u]); u = v; continue; } u = fa[u]; } } int main() { #ifndef ONLINE_JUDGE freopen("B.in","r",stdin); freopen("B.out","w",stdout); #endif scanf("%d%d%d",&n,&m,&d); memset(f, -1, sizeof f); memset(g, -1, sizeof g); for (int i = 1,x; i <= m; ++i) { scanf("%d",&x); f[x].dis = 0; f[x].v = x; } for (int i = 1,x,y; i < n; ++i) { scanf("%d%d",&x,&y); Addedge(x,y); } dfs(1); int ans = 0; for (int i = 1; i <= n; ++i) if (f[i].dis <= d) ++ans; printf("%d ",ans); }
C - Divisor Tree
这题有点意思。观察后不难得出除了root和leave以外的点都是所给的数,而root也有可能是。关键是接下来怎么弄……方法应该有很多。不过我想不出好的方法……
于是去OrzTutorial。其实这个做法挺暴力的……是O(N!)。由于每个所给的数的father也是所给的数或root,并且比它本身来得大。于是我们可以给a[i]排序,然后枚举树的形态。注意细节处理。
#include <cstdio> #include <algorithm> const int N = 9; int ans = 0x7fffffff,n,pnum[N],cnt; long long a[N],root; inline void getpnum() { for (int i = 1; i <= n; ++i) { long long x = a[i]; for (long long j = 2; j*j <= x; ++j) for (;x % j == 0;x /= j) ++pnum[i]; if (x != 1) ++pnum[i]; } } void dfs2(const int idx,const int sum) { if (idx == 0) {ans = std::min(ans,sum + n + 1);return;} for (int i = n; i > idx; --i) { if (a[i] % a[idx] == 0) { a[i] /= a[idx]; dfs2(idx - 1,sum); a[i] *= a[idx]; } } dfs2(idx - 1,sum + pnum[idx]); } bool dfs1(const int idx) { if (idx == 0) {ans = pnum[n] + n;return 1;} for (int i = n; i > idx; --i) { if (a[i] % a[idx] == 0) { a[i] /= a[idx]; if (dfs1(idx - 1)) { a[i] = a[i]*a[idx]; return 1; } a[i] = a[i]*a[idx]; } } return 0; } int main() { #ifndef ONLINE_JUDGE freopen("C.in","r",stdin); freopen("C.out","w",stdout); #endif scanf("%d",&n); for (int i = 1; i <= n; ++i) scanf("%I64d", a + i); std::sort(a + 1,a + 1 + n); root = a[n]; getpnum(); for (int i = 1; i <= n; ++i) if (pnum[i] == 1) ++cnt; //memcpy(back,a,sizeof a); if (!dfs1(n - 1)) dfs2(n,0); printf("%d ",ans - cnt); }
D - GCD Table
这题有点坑……一开始想复杂了。其实:
gcd(i,j) = a[idx] ===> a[idx] | i a[idx] | j
就是这样,仅此而已。
然后可以证明row = lcm(a[1], a[2], a[3], ... ,a[n])
证明也很简单。
首先lcm * x (x > 0)肯定可以。只需考虑gcd(lcm,j) != gcd(lcm * x,j)的情况。
令gcd(lcm * x,j) = d。则d | j, d 不整除 lcm。
若这个位置是合法的话,则有a[i] = d 不整除 lcm。这明显是矛盾的。
对于col:
col = - k + 1 (mod a[k])
然后解线性同余方程组,对答案check即可。
Postscript: 注意乘法的地方,会爆long long。所以用类似快速幂的方法处理。
附图一张(描述了大家被这个trick坑的惨状……)
//greater than max_long_long ... so use quick_multiply (qmult)! #include <cstdio> #include <cmath> const int N = 10000 + 9; typedef long long ll; ll n,m,a[N],K; struct Triple { ll x,y,z; Triple(const ll _x,const ll _y,const ll _z): x(_x),y(_y),z(_z){} }; Triple exgcd(const ll a,const ll b) { // Ax1 + By1 = ax + by // A = b, B = a % b // b * x1 + (a - a/b*b) * y1 = a * x + b * y // a*y1 + b*(x1 - a/b*y1) = a*x + b*y // x = y1, y = x1 - a/b*y1 if (!b) return Triple(1,0,a); Triple last(exgcd(b,a%b)); return Triple(last.y,last.x - a/b * last.y,last.z); } ll gcd(ll a,ll b) { for (ll t;b;) t = a,a = b,b = t % b; return a; } bool calc_row() { ll last = 1; for (int i = 1; i <= K; ++i) { ll tmp = gcd(last,a[i]); if (last / tmp > n / a[i]) return false; last = last / tmp * a[i]; } return true; } ll qmult(ll x,ll k,const ll mod) { ll res = 0,t = 1; if (k < 0) t = -1,k = -k; for (;k;k >>= 1,x = x*2%mod) if (k&1) res = (res + x) % mod; return res*t; } bool calc_col() { // j = -k (mod a[k + 1]) // x = k1 (mod a1) <=> x = a1*p1 + k1 // x = k2 (mod a2) <=> x = a2*p2 + k2 // x = a1*p1 + k1 = a2*p2 + k2 <=> a1*p1 - a2*p2 = k2 - k1 // x = a1*p1 + k1 (mod lcm(a1,a2)) ll lastk = 0,lasta = a[1]; if (lasta + K - 1 > m) return false; for (int i = 2; i <= K; ++i) { ll k = (a[i] - i + 1) % a[i]; Triple s(exgcd(lasta, a[i])); if ((k - lastk) % s.z) return false; const ll mod = a[i] / s.z; const ll times = (k - lastk) / s.z % mod; s.x %= mod; if (fabs((double)s.x * (double)times) > 1e15) s.x = (qmult(s.x,times,mod) + mod)%mod; else s.x = (s.x % mod * times % mod + mod) % mod; lastk += lasta * s.x; lasta = lasta / s.z * a[i]; if ((lastk ? lastk : lasta) + K - 1 > m) return false; } lastk = lastk ? lastk : lasta; for (int i = 1; i <= K; ++i) if (gcd(lasta,lastk + i -1) != a[i]) return false; return true; } void solve() { if (!calc_row() || !calc_col()) puts("NO"); else puts("YES"); } int main() { #ifndef ONLINE_JUDGE freopen("D.in","r",stdin); freopen("D.out","w",stdout); #endif scanf("%I64d%I64d%I64d",&n,&m,&K); for (int i = 1; i <= K; ++i) scanf("%I64d",a + i); solve(); }
E - Optimize!
这题就比较简单了,方法有挺多的吧……我一开始想了个傻叉办法(SPlay……),麻烦得半死……
先sort b[] (从小到大)
令x[i] = min{j | s[i] + b[j] >= h} , y[i] = (x[idx] = i 的个数) , sum[i] = y[i] + y[i + 1] + ... + y[len]
用各种数据结构维护之,然后如果min(len - i + 1 - sum[i]) >= 0 就给ans 加 1。
P.S. 注意细节处理。
#include <cstdio> #include <algorithm> const int N = 150000 + 9; int a[N],min[N*4],tag[N*4],b[N],n,len,h,L,R,D; inline void push_up(const int idx) {min[idx] = std::min(min[idx * 2], min[idx * 2 + 1]);} inline void push_down(const int idx) { if (!tag[idx]) return; tag[idx * 2] += tag[idx]; tag[idx * 2 + 1] += tag[idx]; min[idx * 2] += tag[idx]; min[idx * 2 + 1] += tag[idx]; tag[idx] = 0; } void modify(const int idx,const int l,const int r) { if (L <= l && r <= R) return (void)(min[idx] += D,tag[idx] += D); const int mid = (l + r) / 2; if (tag[idx]) push_down(idx); if (L <= mid) modify(idx * 2, l, mid); if (mid < R) modify(idx * 2 + 1, mid + 1, r); push_up(idx); } int main() { #ifndef ONLINE_JUDGE freopen("E.in","r",stdin); freopen("E.out","w",stdout); #endif scanf("%d%d%d",&n,&len,&h); for (int i = 1; i <= len; ++i) { scanf("%d",b + i); L = R = i; D = len - i + 1; modify(1,1,len); } for (int i = 1; i <= n; ++i) {scanf("%d",a + i); a[i] = h - a[i];} std::sort(b + 1, b + 1 + len); int cnt = 0,ans = 0; for (int i = 1; i <= len; ++i) { if (a[i] > b[len]) {++cnt;continue;} if (i != 1 && a[i] <= 0) continue; L = 1; R = std::lower_bound(b + 1, b + 1 + len,a[i]) - b; D = -1; modify(1,1,len); } if (!cnt && min[1] >= 0) ++ans; for (int i = len + 1; i <= n; ++i) { if (a[i - len] <= b[len]) { L = 1; R = std::lower_bound(b + 1, b + 1 + len,a[i - len]) - b; D = 1; modify(1,1,len); }else --cnt; if (a[i - len + 1] <= 0) { L = 1; R = std::lower_bound(b + 1, b + 1 + len,a[i - len + 1]) - b; D = -1; modify(1,1,len); } if (a[i] > b[len]) {++cnt;continue;} if (a[i] > 0) { L = 1; R = std::lower_bound(b + 1, b + 1 + len,a[i]) - b; D = -1; modify(1,1,len); } if (!cnt && min[1] >= 0) ++ans; } printf("%d ",ans); }