Educational Codeforces Round 81 (Rated for Div. 2)
A - Display The Number
题意:给n根火柴,拼出最大的数字。
题解:肯定是数字越多越大,所以尽可能多拿最便宜的2根火柴一个“1”,多余的肯定是拿一个“7”,由于n>=2,没有特例。
void test_case() {
int n;
scanf("%d", &n);
if(n % 2 == 1) {
printf("%d", 7);
n -= 3;
}
while(n) {
printf("%d", 1);
n -= 2;
}
printf("\n");
}
C - Obtain The String
题意:给两个字符串s和t,要求从t里面选尽可能少的次数,每次选t的一个子序列,然后接在初始为空的字符串z的末尾,用最少的次数使得z变成s。
题解:由于每次选的是子序列,所以每次可以贪心匹配最长的串,所以可以整出一个显而易见的dp。nxt[i][j]表示[i,tl]中的下一个字母j的下标,这个可以倒着直接转移出来,然后枚举s的每个字符,假如还能往后匹配就往后匹配,否则必须从头开始匹配。
D - Same GCDs
题意:给定 \(a\) 和 \(m\) ,求 \(\sum\limits_{i=a}^{a+m-1}[gcd(i,m)=gcd(a,m)]\) 。
首先设 \(g=gcd(a,m)\) 那么求 \(\sum\limits_{i=a}^{a+m-1}[gcd(i,m)=g]\) 。
即 \(\sum\limits_{i=\lfloor\frac{a}{g}\rfloor}^{\lfloor\frac{a+m-1}{g}\rfloor}[gcd(i,\frac{m}{g})=1]\) 。
变成求一个经典问题, \(n\) 以内与 \(m\) 互质的数的个数。 \(\sum\limits_{i=1}^{n}[gcd(i,m)=1]\) 。
去模板库里面掏出这个问题的接口,调用可以得到结果。
其实由于 \(gcd(a,m)=gcd(a\mod m,m)\) (辗转相除法?欧几里得算法?),
\(\sum\limits_{i=a}^{a+m-1}[gcd(i,m)=g]\) 即 \(\sum\limits_{i=1}^{m}[gcd(i,m)=g]\) 除以g后即为为欧拉函数的定义 \(\varphi(\frac{m}{g})\)。
去模板库里面掏出这个问题的接口,调用可以得到结果。
E - Permutation Separation
题意:给一个[1,n]的permutation,称为A。每个数字带有一个权重p,要求把序列初始分为两个非空的前缀L和后缀R,然后搬动其中的元素使得P中的每个元素小于S中的每个元素,或其中之一为空。求最小代价。
题解:有一个显然的n^2暴力,枚举切割前后缀的位置,然后再枚举搬动元素的分界线。昨天晚上学长提了一下线段树,想了一天终于有点明白了,用线段树可以快速转移出不同搬动元素的分界线的最小值,然后只需要枚举切割前后缀的位置就可以了。
从最简单的情况入手:设一棵n个叶子的线段树st,其中每个节点[l,r]表示把[l,r]区间内的数分为前后两块所需要的最小代价cost,初始每个数字叶子会被分出自己的初始属性L或者R,当两个L叶子合并或者两个R叶子合并不需要任何代价。左边是L右边是R的节点本身就合法,合并也不需要任何代价,左边是R右边是L的话,要么把L移动到R,要么把R移动到L,代价就是两者cost的min。
由此出发,考虑更一般的情况设cost(l,r)表示把[l,r]区间分为两堆不交叉的L和R的最小代价,costL(l,r)为把[l,r]中所有初始在R的集合搬到L的代价(维护就是简单加法),costR(l,r)同理。
那么当区间[l,m],[m+1,r]合并,可以口胡出,分界线必定在[l,m]或者[m+1,r]之中。假如分解线在[l,m]中,那么cost(l,r)=cost(l,m)+costR(m+1,r)(把前缀中的大的元素全部移动到后缀),假如分界线在[m+1,r]中,同理cost(l,r)=cost(m+1,r)+costL(l,m)。
所以每次就把原来的permutation里面的分界线元素的costL和costR属性取反,只需要在线段树里面Update一下。
int n;
int valL[200005], valR[200005];
int a[200005];
struct SegmentTree {
#define ls (o<<1)
#define rs (o<<1|1)
static const int MAXN = 200000;
ll cost[(MAXN << 2) + 5];
ll costL[(MAXN << 2) + 5];
ll costR[(MAXN << 2) + 5];
void PushUp(int o) {
cost[o] = min(cost[ls] + costR[rs], cost[rs] + costL[ls]);
costL[o] = costL[ls] + costL[rs];
costR[o] = costR[ls] + costR[rs];
}
void Build(int o, int l, int r) {
if(l == r) {
cost[o] = 0;
costL[o] = valL[l];
costR[o] = valR[l];
//printf("[%d,%d],cost=%d costL=%d costR=%d\n",l,r,cost[o],costL[o],costR[o]);
} else {
int m = l + r >> 1;
Build(ls, l, m);
Build(rs, m + 1, r);
PushUp(o);
//printf("[%d,%d],cost=%d costL=%d costR=%d\n",l,r,cost[o],costL[o],costR[o]);
}
}
void Update(int o, int l, int r, int q) {
if(l == r) {
cost[o] = 0;
costL[o] = valL[l];
costR[o] = valR[l];
//printf("[%d,%d],cost=%d costL=%d costR=%d\n",l,r,cost[o],costL[o],costR[o]);
} else {
int m = l + r >> 1;
if(q <= m)
Update(ls, l, m, q);
if(q >= m + 1)
Update(rs, m + 1, r, q);
PushUp(o);
//printf("[%d,%d],cost=%d costL=%d costR=%d\n",l,r,cost[o],costL[o],costR[o]);
}
}
ll Query(int o, int l, int r, int ql, int qr) {
if(ql <= l && r <= qr) {
return cost[o];
} else {
int m = l + r >> 1;
ll res = LINF;
if(ql <= m)
res = Query(ls, l, m, ql, qr);
if(qr >= m + 1)
res = min(res, Query(rs, m + 1, r, ql, qr));
return res;
}
}
#undef ls
#undef rs
} st;
void test_case() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for(int i = 1; i <= n; ++i)
scanf("%d", &valL[a[i]]);
st.Build(1, 1, n);
ll ans = LINF;
for(int i = 1; i < n; ++i) {
valR[a[i]] = valL[a[i]];
valL[a[i]] = 0;
/*for(int j=1;j<=n;++j)
printf(" %d",valL[a[j]]);
puts("");
for(int j=1;j<=n;++j)
printf(" %d",valR[a[j]]);
puts("");*/
st.Update(1, 1, n, a[i]);
ans = min(ans, st.Query(1, 1, n, 1, n));
//printf("ans=%d\n",st.Query(1, 1, n, 1, n));
//puts("---");
}
printf("%lld\n", ans);
}
F - Good Contest
2020东秦ccpc wannfly camp好像见过?错过了一场上橙的机会,不仅如此还在蓝名苟住了?
题意:给一堆n个(至多50个)[li,ri],第i个数等概率出现在[li,ri]之间,求整个序列没有任何逆序的概率。
题解:因为至多50个,所以可以随便乱搞。假如[l,r]的区间不大,可以有一种简单的dp方法:
\(dp[i][j]\) 表示前 \(i\) 个数没有逆序,且最后一个数大小为 \(j\) 时,整个序列没有任何逆序的概率,注意这里最后一位是 \(j\) 的概率应该是 \(1\)。
若 \(j\) 在合法范围内:
\(dp[i][j]=\frac{1}{r_{i-1}-l_{r-1}+1}\sum\limits_{k=0}^{j}dp[i-1][k]\)
否则:
\(dp[i][j]=0\)
可惜这里[l,r]范围好大哦。