概述
暑假集训第5天,本来想着能A了第一题,可以万事大吉了~~
可去万万没有想到的是第一题不知道什么原因,大数据全WA了
,只有60分,和哪些优秀暴力的人没什么区别,最终排名91中第41名……
-------------
顺序
- 1.字符串函数(strfun)
- 2.优秀路径(path)
- 3.树(tree)
1.字符串函数(strfun)
【 题目描述】
给定两个等长的由大写英文字母构成的字符串 a 和 b, 从 a 中选出子串 x, 从 b 中选出
子串 y, 其中 x,y 等长。
定义函数 f(x, y)为满足条件 xi = yi(1 <= I <= |x|)的 i 的个数, 计算 f(x, y)的数学期望。
【输入数据】
第一行一个正整数: n, 表示 a 和 b 的长度。
第二行输入字符串 a。
第三行输入字符串 b。
【输出数据】
输出一个实数表示 f(x, y)的期望, 答案保留 6 位小数。
【样例输入】
2
AB
BA
【样例输出】
0.400000
【样例解释】
x,y 的选择有 5 种情况, 分别是(“A”, “B”), (“A”, “A”), (“B”, “B”), (“B”, “A”), (“AB”, “BA”)。
其中,第 2 对和第 3 对所对应的 f(x,y)等于 1, 其他都是 0, 由于选择每一对的概率都是
1/5,所以 f(x,y)的期望为 1/5 * 0 + 1/5 * 1 + 1/5 * 1 + 1/5 * 0 + 1/5 * 0 = 2/5 = 0.4。
【数据范围】
对于 30%的数据, n <= 100。
对于 60%的数据, n <= 5000。
对于 100%的数据, n <= 2e5。
我的做法和正解一样推出了价值的 式子
m
i
n
(
i
,
j
)
∗
m
i
n
(
n
−
m
i
n
(
i
,
j
)
+
1
)
min(i,j)*min(n-min(i,j)+1)
min(i,j)∗min(n−min(i,j)+1)
然后我用了O(26N)的复杂度,记录前面几次并无关紧要的,虽然还是能过
期望的和=和的期望
考虑每对位置(a, b)对答案的贡献。
又发现每对(x,y)的期望可由每对位置(a,b)的期望和得来。
于是考虑对于第一串的 a 位置,第二串的 b 位置对答案的贡献。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m;
long long k;
char cA[N],cB[N];
int l[N][30],r[N][30];
long long ans;
int main()
{
freopen("strfun.in","r",stdin);
freopen("strfun.out","w",stdout);
int i,j;//价值只会是min(i,j)*min(n-i+1,n-j+1)
scanf("%d%s%s",&n,cA+1,cB+1);
for(i=0;++i<=n;)//对A进行一次前缀和
{
k+=(long long)(n-i+1)*(long long)(n-i+1);
int x=cA[i]-'A'+1;
for(j=0;++j<=26;)l[i][j]=l[i-1][j];
l[i][x]+=i;
}
for(i=n;i>=1;--i)//对A进行后缀
{
int x=cA[i]-'A'+1;
for(j=0;++j<=26;)r[i][j]=r[i+1][j];
r[i][x]+=(n-i+1);
}
for(i=0;++i<=n;)//进行一次价值操作
{
int x=cB[i]-'A'+1;
ans+=(long long)l[i][x]*(n-i+1)+(long long)r[i+1][x]*i;
}
printf("%.6lf",(double)ans/(double)k);
return 0;
}
那么我要问一个很严肃的问题……
这两个输入卡的我半死,第一个输入为什么会挂,为什么和第二个不一样???
不懂不懂
2.优秀路径(path)
【题目描述】
给定一颗大小为 n 的树, 树边有边权,顶点有点权。
Alice 认为一条路径是“优秀的”, 当且仅当路径上相邻边的边权不同。
定义一条路径的价值为路径上所有点的点权和。
求树上所有“优秀的”简单路径的价值和。
【输入数据】
第一行输入一个整数 n, 表示树的大小。
第二行输入 n 个整数, 表示 1-n 号点的点权 ai。
接下来 n-1 行, 每行输入 3 个整数 u, v, w,表示 u, v 之间存在一条边权为 w 的边。
【输出数据】
输出一个整数表示树上所有“优秀的”简单路径的价值和。
【样例输入】
6
6 2 3 7 1 4
1 2 1
1 3 2
1 4 3
2 5 1
2 6 2
【样例输出】
134
【样例解释】
优秀路径分别为{1,2}, {1,2,6}, {1,3}, {1,4}, {2,1,3}, {2,1,4}, {2,5}, {2,6}, {3,1,4}, {3,1,2,6},
{4,1,2,6}, {5,2,6}, 路径的价值依次为 8, 12, 9, 13, 11, 15, 3, 6, 16, 15, 19, 7 。
【数据范围】
对于 30%的数据, n <= 10。
对于 50%的数据, n <= 1000。
对于 100%的数据, n <= 3e5, 1 <= u, v <= n, 1 <= ai, w <= 1e5。
树形 dp。
统计到每个点的合法链的价值和, 合法链的条数, 两条单链在端点处合并即可。
合并时注意判断链与链相接的两条边是否相同。
复杂度 O(n)
====不对
应该是这个:
暴零,暴力打错
先看看暴力怎么打吧~by hkhh点击这里进入hkhh神犇的博客
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e5;
struct node{
int Next,y,v;
}e[2*N+10];
int n,len=0,ans=0;
int b[N+10];
int linkk[2*N];
int a[N+10];
bool vis[N+10];
void insert(int x,int y,int v){
e[++len].Next=linkk[x];
linkk[x]=len;
e[len].y=y,e[len].v=v;
}
void work(int num){
for (int i=1;i<=num;i++) ans+=a[b[i]];
}
void dfs(int x,int lastv,int num){
if (num>1) work(num);
for (int i=linkk[x];i;i=e[i].Next){
int y=e[i].y;
if (vis[y]) continue;
if (e[i].v==lastv) continue;
vis[y]=1;
b[num+1]=y;
dfs(y,e[i].v,num+1);
b[num+1]=0;
}
}
signed main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
scanf("%lld",&n);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
for (int i=1,x,y,z;i<n;i++){
scanf("%lld %lld %lld",&x,&y,&z);
insert(x,y,z),insert(y,x,z);
}
for (int i=1;i<=n;i++) memset(vis,0,sizeof(vis)),b[1]=i,dfs(i,-1,1);
printf("%lld",ans/2);
return 0;
}
正解,是不是就要请教std,wy老师了?
正解代码
#include <cstdio>
typedef long long ll;
const int N = 3e5 + 5;
int n, a[N], tot, first[N], next[N << 1], end[N << 1], cap[N << 1];
int que[N], qn, fa[N], fc[N];
ll ans, cnt[N], sum[N], nc[N], ns[N];
inline int Get() {
char ch;
while ((ch = getchar()) < '0' || ch > '9');
int Num = ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9')
Num = Num * 10 + ch - '0';
return Num;
}
inline void addEdge(int u, int v, int w) {
next[++tot] = first[u], first[u] = tot, end[tot] = v, cap[tot] = w;
next[++tot] = first[v], first[v] = tot, end[tot] = u, cap[tot] = w;
}
int main() {
freopen("path.in", "r", stdin);
freopen("path.out", "w", stdout);
n = Get();
for (int i = 1; i <= n; ++i) a[i] = Get();
for (int i = 1, u, v, w; i < n; ++i)
u = Get(), v = Get(), w = Get(), addEdge(u, v, w);
que[qn = 1] = 1, fa[1] = 0, fc[1] = 0;
for (int ql = 1, u; u = que[ql], ql <= qn; ++ql)
for (int k = first[u], v; v = end[k], k; k = next[k])
if (v ^ fa[u]) fa[v] = u, fc[v] = cap[k], que[++qn] = v;
for (int qr = qn, u; u = que[qr], qr >= 1; --qr) {
ll sur = a[u], cur = 1;
for (int k = first[u], v, w; v = end[k], w = cap[k], k; k = next[k])
if (v ^ fa[u]) {
ans += (sur - ns[w]) * cnt[v] + sum[v] * (cur - nc[w]);
sur += sum[v] + cnt[v] * a[u], cur += cnt[v];
ns[w] += sum[v] + cnt[v] * a[u], nc[w] += cnt[v];
}
cnt[u] = 1;
for (int k = first[u], v, w; v = end[k], w = cap[k], k; k = next[k])
if (v ^ fa[u]) {
ns[w] -= sum[v] + cnt[v] * a[u], nc[w] -= cnt[v];
if (fc[u] ^ w) sum[u] += sum[v], cnt[u] += cnt[v];
}
sum[u] += cnt[u] * a[u];
}
printf("%lld\n", ans);
fclose(stdin);
fclose(stdout);
}
//真是好看 的代码,嗯~
树(tree)
【 题目描述】
有 n 个点从 1 到 n 进行编号, 第 i 个点的限制为度数不能超过 ai。
对每个 s(1 <= s <= n), 问从这 n 个点中选出一些点组成大小为 s 的有标号无根树的方
案数。
【输入数据】
第一行一个整数 n。
第二行 n 个整数表示 ai。
【输出数据】
输出一行n个整数, 第i个整数表示s = i时的答案。答案模1004535809=479*(2^21)+1。
【样例输入】
3 2
2 1
【样例输出】
3 3 2
【数据范围】
对于 20%的数据, n <= 6。
对于 60%的数据, n <= 50。
对于 100%的数据, n <= 100。
本题严重超纲,既然要涉及到prufer 序???
全场只有一人学过,也就这一人AC了这题
不知道prufer点击这里
prufer 序的编码方式:迭代删点
设树的大小为 n
n = 2 时, prufer 序列为空
n > 2 时, 每次当前标号最小的叶子结点, 并将与叶子节点相连的结点加入序列中。
prufer 序转树
设序列长度为 n
n = 0 时,显然是一个{1-2}
n > 0 时, 令设一个集合 G= {1, 2, 3, …, n}, 每次找出集合中最小的不在当前 prufer
序中出现的点, 将它和 prufer 序的第一个点连边, 在集合中删去该点, 删去 prufer 序的
第一位。
prufer 序和树存在一一对应的关系。
#include <cstdio>
typedef long long ll;
const int Mod = 1004535809;
const int MaxN = 110;
int n, dp[MaxN][MaxN][MaxN], A[MaxN], fac[MaxN], Ie[MaxN];
int pow(int x, int k) {
ll res = 1, r = x;
for (; k; k >>= 1, r = r * r % Mod)
if (k & 1) res = res * r % Mod;
return res;
}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &A[i]);
if (A[i] >= n) A[i] = n - 1;
}
fac[0] = 1;
for (int i = 1; i <= n; ++i) fac[i] = (ll)fac[i - 1] * i % Mod;
Ie[n] = pow(fac[n], Mod - 2);
for (int i = n; i; --i) Ie[i - 1] = (ll)Ie[i] * i % Mod;
dp[0][0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < i; ++j) {
for (int k = 0; k <= n; ++k)
if (dp[i - 1][j][k]) {
if ((dp[i][j][k] += dp[i - 1][j][k]) >= Mod) dp[i][j][k] -= Mod;
for (int c = 0; c < A[i]; ++c) {
if (k + c > n) break;
if ((dp[i][j + 1][k + c] += (ll)dp[i - 1][j][k] * Ie[c] % Mod) >= Mod) dp[i][j + 1][k + c] -= Mod;
}
}
}
}
printf("%d ", n);
for (int i = 2; i <= n; ++i) printf("%d ", (ll)dp[n][i][i - 2] * fac[i - 2] % Mod);
fclose(stdin);
fclose(stdout);
}
那好像就结束了~
所以