A : 打表找规律
B : 数学期望
C : 拓扑排序+DP
D : 状压DP
E : 莫队+树状数组+卡常数
F : Unfinished
听说省选推迟了一个月,整个人都不好了。放假打了一场51Nod,感觉整个人更不好了。
A 1718 “多项式”
能出在第一题的多项式肯定是有奇技淫巧的,打表找了一发规律,发现它的循环节是-1,-2,-1,1,2,1,…
#include<cstdio>
using namespace std;
namespace runzhe2000
{
long long n; int a[6] = {-1,-2,-1,1,2,1};
void main()
{
scanf("%lld",&n);
printf("%d",a[(n+4)%6]);
}
}
int main()
{
runzhe2000::main();
}
B 1756 谷歌的恐龙
设这一轮 E1 是选完之后就死,这一轮的期望。 E0 是选完之后活的期望。 P=MN
瞎JB推一下,答案 = E1+(1−P)∗E0P
要开long double和输入优化- -
其实好像可以继续推下去的,然而不管啦……反正卡过去了
#include<cstdio>
#define N 10000005
#define getc() ((S == T) && (T = (S = B) + fread(B, 1, MaxBuff, stdin), S == T) ? 0 : *S++)
using namespace std;
namespace runzhe2000
{
typedef long double ld;
typedef double db;
const int MaxBuff = 1 << 20;
char B[MaxBuff], *S = B, *T = B;
int read()
{
int r = 0; char c = getc();
for(; c < '0' || c > '9'; c = getc());
for(; c >='0' && c <='9'; r = r*10+c-'0', c = getc());
return r;
}
int n, m, a[N];
ld E0,E1, P;
void main()
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= m; i++) a[read()] = 1;
for(int i = 0; i < n; i++)
{
if(a[i]) E1 += (ld) i / m;
else E0 += (ld) i / (n-m);
}
ld P = (ld)m / n;
printf("%lf\n",(db)(E1+(1-P)*E0/P));
}
}
int main()
{
runzhe2000::main();
}
C 1815 调查任务
做得很虚的一道题。不知道我这个做法是不是对的,原来的代码被C题加强数据的第16个hack点卡了好久,多记了一个状态才过的。不知道会不会有别的数据能卡(如果有请务必告诉我^_^),毕竟这种图论题数据类型多种多样…
题意就是最大化路径的次大值。先缩强,记下强联通块内最大值和次大值。
记f[i]表示从S出发到i,能经过的最大值,这个直接DP就行了。
记g[i]表示从S出发到i,路径上经过的最大的次大值,也就是答案所求。考虑转移,大力分类讨论,设j能转移给i。
- 如果g[i]选择了点j及j之前的次大值,那直接从g[j]转移过来。
- 如果g[i]选择了点j及j之前的最大值,当且仅当i自身是路径最大值,这个用f[i]判一下。且此时那个被转移的最大值一定是f[j],(其实有反例,应该就是那个第16个点,也注意特判此时f[j]=f[i],需要多记h[j]表示除最大值以外的第二大,若f[j]=f[i]则从h[j]转移之类的)。
- 如果g[i]选择了i点自身的次大值或最大值,当且仅当f[i]大于i点自身的次大值或最大值,直接转移。
不保证上面口胡的东西是对的,因为调完代码之后我已经忘了我之前在想了什么了……
#include<cstdio>
#include<queue>
#include<cstring>
#define N 400005
#define M 2000005
#define cmin(u,v) ((u)>(v)?(u)=(v):0)
#define cmax(u,v) ((u)<(v)?(u)=(v):0)
using namespace std;
namespace runzhe2000
{
int n, m, q, s, ecnt, a[N], last[N], bcnt, bel[N], mx[N], mx2[N], deg[N], tdeg[N], f[N], g[N], h[N];
int timer, dfn[N], low[N], sta[N], insta[N], stacnt;
struct edge{int next, to;}e[M<<1];
struct pdge{int a, b;}pe[M];
void addedge(int a, int b)
{
e[++ecnt] = (edge){last[a], b};
last[a] = ecnt;
}
void tarjan(int x)
{
sta[++stacnt] = x; insta[x] = 1; dfn[x] = low[x] = ++timer;
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to;
if(!dfn[y]) {tarjan(y); cmin(low[x], low[y]);}
else if(insta[y]) cmin(low[x], dfn[y]);
}
if(dfn[x] == low[x])
{
++bcnt; int y;
do
{
y = sta[stacnt--];
bel[y] = bcnt; insta[y] = 0;
if(a[y] > mx[bcnt]) mx2[bcnt] = mx[bcnt], mx[bcnt] = a[y];
else if(a[y] > mx2[bcnt] && a[y] != mx[bcnt]) mx2[bcnt] = a[y];
}
while(y != x);
}
}
void dp()
{
{ // dp_mx
memcpy(tdeg, deg, sizeof(deg)); queue<int> q; q.push(bel[s]);
for(; !q.empty(); )
{
int x = q.front(); q.pop(); cmax(f[x], mx[x]);
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to;
if(f[x] > f[y])
{
h[y] = f[y];
f[y] = f[x];
}
else if(f[x] != f[y] && f[x] > h[y]) h[y] = f[x];
if(!--tdeg[y]) q.push(y);
}
}
}
{ // dp_mx2
memcpy(tdeg, deg, sizeof(deg)); queue<int> q; q.push(bel[s]);
for(; !q.empty(); )
{
int x = q.front(); q.pop();
if(mx[x] != f[x]) cmax(g[x], mx[x]);
else cmax(g[x], mx2[x]);
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to;
cmax(g[y], g[x]);
if(f[y] == mx[y] && f[x] != f[y]) cmax(g[y], f[x]);
if(f[y] == mx[y] && h[x] != f[y]) cmax(g[y], h[x]);
if(!--tdeg[y]) q.push(y);
}
}
}
}
void main()
{
scanf("%d%d%d%d",&n,&m,&q,&s);
for(int i = 1; i <= n; i++) scanf("%d",&a[i]);
for(int i = 1, a, b; i <= m; i++)
{
scanf("%d%d",&a,&b);
addedge(a, b);
pe[i] = (pdge){a, b};
}
tarjan(s); ecnt = 0;
memset(last, 0, sizeof(last));
for(int i = 1; i <= m; i++)
{
if(bel[pe[i].a] != bel[pe[i].b] && bel[pe[i].a])
{
addedge(bel[pe[i].a], bel[pe[i].b]);
deg[bel[pe[i].b]]++;
}
}
deg[bel[s]] = 0;
dp();
for(int i = 1; i <= q; i++)
{
int x; scanf("%d",&x);
printf("%d ",dfn[x]?g[bel[x]]:-1);
}
}
}
int main()
{
runzhe2000::main();
}
D 1683 最短路
写了比较长的一段题解,然后消失了,好气啊。那就再口胡一遍吧。n很小,对列状压,我们记 f[i][j1][j2]...[jn] 表示到第i列的第1,2,…n行的最短路分别是j1,j2,…jn时的方案数,枚举下一列的状态转移。j1,j2…jn的状态数并不多,因为显然相邻两格最短路不超过1。j1,j2,…jn可以哈希起来。转移要先预处理,要不然可能会T。
#include<cstdio>
#include<algorithm>
#define N 7
#define M 105
#define H 250
using namespace std;
namespace runzhe2000
{
const int INF = 1<<29;
int n, m, mod, sta, tot, a[N], b[N], d[N], f[M][M+N][H], ans[M+N];
struct trans{int to, up;}tr[H][1<<N];
int packin(int *a)
{
int ret = 0;
for(int i = 2; i <= n; i++) ret = ret * 3 + (a[i] + 1);
return ret;
}
int packout(int *a, int r)
{
for(int i = n; i >= 2; i--) a[i] = r % 3 - 1, r /= 3;
return r;
}
void main()
{
scanf("%d%d%d",&n,&m,&mod); sta = 1<<n;
for(int i = 2; i <= n; i++) a[i] = 1; tot = packin(a);
for(int i = 0; i <= tot; i++)
{
packout(a, i);
for(int k = 2; k <= n; k++) a[k] += a[k-1];
for(int j = 0; j < sta; j++)
{
for(int k = 1; k <= n; k++) b[k] = (j>>(k-1))&1;
for(int k = 1; k <= n; k++) d[k] = a[k] + b[k];
for(int k = 1; k <= n; k++)
{
for(int h = k-1; h >= 1; h--) d[h] = min(d[h], d[h+1] + b[h]);
for(int h = k+1; h <= n; h++) d[h] = min(d[h], d[h-1] + b[h]);
}
for(int k = n; k > 1; k--) d[k] -= d[k-1];
tr[i][j] = (trans){packin(d), d[1]};
}
}
for(int i = 0; i < sta; i++)
{
for(int k = 1; k <= n; k++) b[k] = (i>>(k-1))&1;
int s = packin(b);
f[1][b[1]][s]++;
}
for(int i = 1; i < m; i++)
for(int j = 0, jj = n+m; j < jj; j++)
for(int k = 0; k <= tot; k++) if(f[i][j][k])
for(int s = 0; s < sta; s++)
(f[i+1][j+tr[k][s].up][tr[k][s].to] += f[i][j][k]) %= mod;
for(int i = 0; i <= m; i++)
for(int j = 0; j <= tot; j++)
{
packout(a, j);
for(int k = 2; k <= n; k++) a[k] += a[k-1];
(ans[i+a[n]] += f[m][i][j]) %= mod;
}
for(int i = 0, ii = n+m; i < ii; i++) printf("%d\n",ans[i]);
}
}
int main()
{
runzhe2000::main();
}
E 1592 数列积
这是一个不知道正解的人的卡常数(log)的心酸历程
绝对值很麻烦,肯定要拆,那么就分开考虑 ai<aj 和 ai>aj 的。考虑到这个信息很难合并,想想莫队。
考虑加入一个元素之后发生了什么变化。设在区间 [l,r] 的尾部加了一个 a[j] 进来,其它位置的删/加类似。
我们先假设只有 ai<aj 的情况,那答案就会增加 ∑ri=l(aj−ai)(j−i) ,那我们可以维护四个信息()详见代码)来快速算出这个增量。对于 ai>aj ,维护另一套信息即可。
然后我写了一个主席树,这样是 O(nn√ log n) 。卡了一个下午的常数,愣是没卡过去,当时整个人都不好了。
后来考虑 O(nn√) 的做法,感觉可以上分块数据结构,也不知道对不对。
之后得知把主席树改成树状数组就能快很多。好吧,我naive了,改改改,拼命加优化。
再后来,我就也卡过去了…..
#include<cmath>
#include<ctime>
#include<cstdio>
#include<algorithm>
#define N 50005
#define lowbit(_i) (_i&-_i)
#define R register
using namespace std;
namespace runzhe2000
{
typedef unsigned long long ull;
int read()
{
int r = 0; char c = getchar();
for(; c < '0' || c > '9'; c = getchar());
for(; c >='0' && c <='9'; r = r*10+c-'0', c = getchar());
return r;
}
int n, S, qcnt, mx, a[N], block[N], ccnt;
ull out[N];
struct que
{
int l, r, id;
bool operator < (const que &that) const
{
return block[l] != block[that.l]
? block[l] < block[that.l]
: (block[l] & 1) ? r < that.r : that.r < r;
}
}q[N];
struct BIT
{
ull t[N<<1];
ull ask(R int x){R ull r = 0; for(; x; x -= lowbit(x)) r += t[x]; return r;}
void mod(R int x, R ull v){for(; x <= mx; x += lowbit(x)) t[x] += v;}
} tsum, tsum_i, tsum_a, tsum_1;
ull add(int j)
{
int pos = a[j];
ull ret = 0, sum = tsum.ask(pos), sum_i = tsum_i.ask(pos), sum_a = tsum_a.ask(pos), sum_1 = tsum_1.ask(pos);
ret += -sum+pos*sum_i+(j-n)*(sum_1*pos-sum_a);
sum = tsum.ask(mx)-sum; sum_i = tsum_i.ask(mx)-sum_i; sum_a = tsum_a.ask(mx)-sum_a; sum_1 = tsum_1.ask(mx)-sum_1;
ret -= -sum+pos*sum_i+(j-n)*(sum_1*pos-sum_a);
tsum.mod(pos, (ull)(n-j)*pos);
tsum_i.mod(pos, n-j);
tsum_a.mod(pos, pos);
tsum_1.mod(pos, 1);
return ret;
}
ull del(int j)
{
int pos = a[j];
tsum.mod(pos, (ull)(j-n)*pos);
tsum_i.mod(pos, -n+j);
tsum_a.mod(pos, -pos);
tsum_1.mod(pos, -1);
ull ret = 0, sum = tsum.ask(pos), sum_i = tsum_i.ask(pos), sum_a = tsum_a.ask(pos), sum_1 = tsum_1.ask(pos);
ret -= -sum+pos*sum_i+(j-n)*(sum_1*pos-sum_a);
sum = tsum.ask(mx)-sum; sum_i = tsum_i.ask(mx)-sum_i; sum_a = tsum_a.ask(mx)-sum_a; sum_1 = tsum_1.ask(mx)-sum_1;
ret += -sum+pos*sum_i+(j-n)*(sum_1*pos-sum_a);
return ret;
}
void main()
{
n = read(); S = sqrt((double)n);
for(int i = 1; i <= n; i++) a[i] = read(), block[i] = i % S == 1 ? block[i-1]+1 : block[i-1], mx < a[i] ? mx = a[i] : 0; mx++;
qcnt = read(); for(int i = 1; i <= qcnt; i++) q[i].l = read(), q[i].r = read(), q[i].id = i;
sort(q+1, q+1+qcnt); R int lef = 1, rig = 0, l, r; ull ans = 0;
for(int i = 1; i <= qcnt; i++)
{
l = q[i].l, r = q[i].r;
for(; l < lef; lef--) ans -= add(lef-1);
for(; rig < r; rig++) ans += add(rig+1);
for(; lef < l; lef++) ans -= del(lef);
for(; r < rig; rig--) ans += del(rig);
out[q[i].id] = ans;
}
for(int i = 1; i <= qcnt; i++) printf("%llu\n",out[i]);
}
}
int main()
{
runzhe2000::main();
}