鸽子又回来了!(震声
提前四分钟跑路(
C题:
简单DP一发就完事。RE是因为,我访问到了DP[3][?],但是我的dp数组第一维大小是3。wszz。
#include<bits/stdc++.h>
using namespace std;
int n;
int dp[300][1010];
int val[300][1010];
int main() {
scanf("%d", &n);
for(int i=1;i<=2;++i) {
for(int j=1;j<=n;++j) {
scanf("%d", &val[i][j]);
}
}
memset(dp, -0x3f, sizeof(dp));
dp[1][1] = val[1][1];
for(int i=1;i<=2;++i) {
for(int j=1;j<=n;++j) {
dp[i][j+1] = max(dp[i][j+1], dp[i][j] + val[i][j+1]);
dp[i+1][j] = max(dp[i+1][j], dp[i][j] + val[i+1][j]);
}
}
cout<<dp[2][n];
return 0;
}
D题:
本来想写带权并查集的,然后发觉不太会。想了想之后发现,他好像只需要判个是否合法,并不需要输出在第几个挂的。那么我们直接建图连边,然后随便抓个点往下dfs就完事。
具体的,用
(
u
,
v
,
l
e
n
)
(u, v, len)
(u,v,len)和
(
v
,
u
,
−
l
e
n
)
(v,u,-len)
(v,u,−len)来描述
v
a
l
[
u
]
−
v
a
l
[
v
]
=
l
e
n
val[u]-val[v]=len
val[u]−val[v]=len这件事,然后如果合法,这图里头两点间的所有路径的长度就应该是个定值。抓个点dfs下去,对于每个边,如果他的目的地被dfs过了,那么判一下是否满足关系。
#include<bits/stdc++.h>
using namespace std;
struct edge {
int to, len;
};
vector<edge>ed[100100];
bool vis[100010];
long long dist[100010];
int n, m;
bool OK = 1;
inline void ade(int l, int r, int val) {
ed[l].push_back({r, val});
ed[r].push_back({l, -val});
}
void dfs(int nw, long long len) {
vis[nw] = 1, dist[nw] = len;
for(int i=0;i<ed[nw].size();++i) {
int tar = ed[nw][i].to, nlen = ed[nw][i].len;
if(!vis[tar])
dfs(tar, len + nlen);
else
if(dist[tar] != len + nlen)
OK = 0;
}
return;
}
int main() {
scanf("%d%d", &n, &m);
for(int i=1;i<=m;++i) {
int l, r, val;
scanf("%d%d%d", &l, &r, &val);
ade(l, r, val);
}
for(int i=1;i<=n;++i) {
if(!vis[i])
dfs(i, 0);
}
printf(OK ? "Yes" : "No");
return 0;
}
E题:
这E题不比F题劲多了。
正难则反,一发最短路计数统计出最短路数量,先无脑选,然后干掉不合法的。
“不能在点或边相遇”,想到两种情况都要判。
在点相遇很简单,
d
i
s
t
[
S
]
[
p
o
s
]
=
d
i
s
t
[
T
]
[
p
o
s
]
dist[S][pos]=dist[T][pos]
dist[S][pos]=dist[T][pos],且有
d
i
s
t
[
S
]
[
p
o
s
]
+
d
i
s
t
[
T
]
[
p
o
s
]
=
d
i
s
t
[
S
]
[
T
]
dist[S][pos]+dist[T][pos]=dist[S][T]
dist[S][pos]+dist[T][pos]=dist[S][T]。
经过这个点的方案数咋统计呢?两遍dij,用
S
S
S到
p
o
s
pos
pos 的方案数量乘上
p
o
s
pos
pos到
T
T
T的方案数量。
然后考虑在边上撞车,显然撞车的时间点是
d
i
s
t
[
S
]
[
T
]
2
\frac{dist[S][T]}{2}
2dist[S][T]。
对于一个边
(
l
,
r
,
l
e
n
)
(l, r, len)
(l,r,len),如果有
d
i
s
t
[
S
]
[
l
]
<
d
i
s
t
[
S
]
[
T
]
2
dist[S][l]<\frac{dist[S][T]}{2}
dist[S][l]<2dist[S][T],且
d
i
s
t
[
r
]
[
T
]
<
d
i
s
t
[
S
]
[
T
]
2
dist[r][T]<\frac{dist[S][T]}{2}
dist[r][T]<2dist[S][T],且
d
i
s
t
[
S
]
[
l
]
+
l
e
n
+
d
i
s
t
[
r
]
[
T
]
=
d
i
s
t
[
S
]
[
T
]
dist[S][l]+len+dist[r][T]=dist[S][T]
dist[S][l]+len+dist[r][T]=dist[S][T],那么说明存在在这条边撞上的路径。
乘一乘,减一减,完事。
哦对,WA了一万发是因为,判在边上撞车那里,我成功把代表
d
i
s
t
[
S
]
[
T
]
dist[S][T]
dist[S][T]的变量
L
e
n
Len
Len打成了边长
l
e
n
len
len。
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct edge {
int to, len;
};
vector<edge>ed[100010];
long long disS[100010], disT[100010], cntS[100010], cntT[100010], vis[100010];
int n, m, S, T;
const int mod = 1000000007;
long long ans = 0;
inline void ade(int l, int r, int val) {
ed[l].push_back({r, val});
ed[r].push_back({l, val});
}
inline void dij(int SS, int TT, long long *dist, long long *cnt) {
for(int i=1;i<=n;++i) {
dist[i] = 0x3f3f3f3f3f3f3f3f;
cnt[i] = 0;
vis[i] = 0;
}
dist[SS] = 0, cnt[SS] = 1;
priority_queue<pair<long long, int> >sth;
sth.push({0, SS});
while(!sth.empty()) {
int nw = sth.top().second; sth.pop();
if(vis[nw]) continue;
vis[nw] = 1, cnt[nw] %= mod;
for(int i=0;i<ed[nw].size();++i) {
int tar = ed[nw][i].to, nlen = ed[nw][i].len;
if(dist[tar] > dist[nw] + nlen) {
dist[tar] = dist[nw] + nlen, cnt[tar] = cnt[nw];
sth.push(make_pair(-dist[tar], tar));
}
else if(dist[tar] == dist[nw] + nlen) {
cnt[tar] += cnt[nw], cnt[tar] %= mod;
}
}
}
return;
}
signed main() {
scanf("%lld%lld", &n, &m);
scanf("%lld%lld", &S, &T);
int l, r, val;
for(int i=1;i<=m;++i) {
scanf("%lld%lld%lld", &l, &r, &val);
ade(l, r, val);
}
dij(S, T, disS, cntS);
dij(T, S, disT, cntT);
long long Len = disS[T];
if(cntS[T] != cntT[S])
cout<<"FUCK";
ans += 1ll * cntS[T] * cntS[T];
ans %= mod, ans += mod, ans %= mod;
for(int i=1;i<=n;++i) {
if(disS[i] + disT[i] == Len && disS[i] + disS[i] == Len && disS[i] == disT[i]) {
long long tot = cntS[i] * cntT[i];
tot %= mod;
ans -= 1ll * tot * tot;
ans %= mod, ans += mod, ans %= mod;
}
}
for(int i=1;i<=n;++i) {
for(int j=0;j<ed[i].size();++j) {
int l = i, r = ed[i][j].to;
int len = ed[i][j].len;
if(disS[l] + len + disT[r] == Len && 2 * disS[l] < Len && 2 * disT[r] < Len) {
long long tot = cntS[l] * cntT[r];
tot %= mod;
ans -= 1ll * tot * tot;
ans %= mod, ans += mod, ans %= mod;
}
}
}
cout<<ans;
return 0;
}
F题:
开心快乐计数题。
假设
[
l
,
r
]
[l, r]
[l,r]里头有
n
n
n个数字。若这
i
i
i个数字的
f
f
f值都一样,那么就显然很好搞。
如果不一样的话,我们发觉如果有两种,那么还稍微好搞一点,如果种类多于两种,就不那么好搞了。
然后发觉,如果多于两种,肯定是完整包含了一大坨东西,这一坨东西显然最多是7位数。
然后就小数字搞个双指针扫一扫,大数字判一判。
判的时候,枚举
[
r
,
l
]
[r,l]
[r,l]里头有多少个数字,令他为
i
i
i。如果
n
%
i
=
=
0
n\%i==0
n%i==0,那么说明存在 “
[
l
,
r
]
[l,r]
[l,r]里头
f
f
f值一样” 的情况,快速幂算一算方案数。如果
n
%
i
!
=
0
n\%i!=0
n%i!=0,那么说明有个分界点,显然这个只有一种情况。
#include<bits/stdc++.h>
using namespace std;
int f[30000000];
const int maxn = 29999999;
int n;
long long ans = 0;
const int mod = 1000000007;
long long fpow(long long di, long long top) {
long long ret = 1;
while(top) {
if(top % 2)
ret = ret * di % mod;
di = di * di % mod;
top /= 2;
}
return ret;
}
int main() {
f[1] = 1, f[10] = 2, f[100] = 3, f[1000] = 4, f[10000] = 5, f[100000] = 6, f[1000000] = 7, f[10000000] = 8;
for(int i=1;i<=maxn;++i) {
f[i] = f[i] == 0 ? f[i-1] : f[i];
}
int nr = 1, nsum = 1;
cin>>n;
while(nsum < n)
nsum += f[++nr];
ans += (nsum == n);
for(int i = 1; i < 10000000 - 1;++i) {
nsum -= f[i];
while(nsum < n) {
nsum += f[++nr];
}
ans += (nsum == n);
}
for(int i = 1; i <= n / 8; ++i) {
if(n % i == 0) {
long long tot = fpow(10, n/i);
long long res = fpow(10, n/i - 1);
long long fin = tot - res - i;
ans += fin + 1;
ans %= mod, ans += mod, ans %= mod;
}
else {
ans++;
ans %= mod, ans += mod, ans %= mod;
}
}
cout<<ans;
return 0;
}