(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\n",ans);
// return 0;
// }
// }
i = 1ll*(n - m)*k;
//printf("%d\n",i);
if (i > n) {
printf("%d\n",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\n",ans1);
(ans1 += MOD)%=MOD;
i = (n -i)/k * k + i;
(ans1 += n - i)%=MOD;
printf("%d\n",(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\n",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\n",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\n",ans);
}