A TLE Week
Nowcoder练习赛61
D.最短路变短了
一个$n$个节点$m$条边的有向图,有$q$次查询,每次查询一条边$(u,v)$,如果把$(u,v)$反过来,判断$1$到$n$
的最短路会不会变短。
$(1≤n≤100000),(1≤m≤200000), (1≤q≤200000)$
solution:
先跑一边最短路,令$da_{i}$代表$1$到$i$的最短路,再反向建边以$n$为起点跑一边最短路,$db_{i}$为$i$到$n$的最短路,
对于查询的$(u,v,w)$,判断是否$da_{v}+w+db_{u}<da_{n}$,如果满足,则最短路会变短。
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88/*
* @author: codancer
* @createTime: 2020-04-10, 20:13:32
*/
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
typedef vector<int> VI;
typedef vector<ll> VII;
typedef pair<int,int> pii;
const int N = 200010;
struct node{
int id;
ll dis;
};
int n,m,u,v,x;
ll w;
bool operator<(node a,node b){
return a.dis>b.dis;
}
struct graphs{
vector<pair<int,ll>> G[N];
void addedge(int u,int v,ll w){
G[u].pb({v,w});
}
ll dis[N];
priority_queue<node> q;
bool vis[N];
void bfs(int st){
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
dis[i]=1e17;
}
dis[st]=0;
q.push({st,0});
while(!q.empty()){
node rt=q.top();q.pop();
int u=rt.id;
if(vis[u]) continue;
vis[u]=1;
for(auto adj:G[u]){
int v=adj.first;
ll w=adj.second;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
q.push({v,dis[v]});
}
}
}
}
}A,B;
pair<pair<int,int>,ll> edges[N];
int main(){
scanf("%d %d",&n,&m);
rep(i,1,m){
scanf("%d %d %lld",&u,&v,&w);
edges[i]={{u,v},w};
A.addedge(u,v,w);
B.addedge(v,u,w);
}
A.bfs(1);
B.bfs(n);
ll ans=A.dis[n];
int q;
scanf("%d",&q);
while(q--){
scanf("%d",&x);
int uu=edges[x].first.first;
int vv=edges[x].first.second;
ll ww=edges[x].second;
ll fi=A.dis[vv];
ll se=B.dis[uu];
if(fi+se+ww<ans){
puts("YES");
}else{
puts("NO");
}
}
return 0;
}Leetcode 30days challenge
Middle of the Linked List
寻找链表的中间节点,这是一个非常经典的问题,设置两个指针,一个每次跳一步,另一个每次跳两步,直到快的那个为空,慢的那个所在的节点即为答案。
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* middleNode(ListNode* head) {
ListNode *A=head;
ListNode *B=head;
while(B!=NULL){
B=B->next;
if(B==NULL){
return A;
}
A=A->next;
B=B->next;
}
return A;
}
};给 N x 3 网格图涂色的方案数
题面:
- 你有一个 $n \cdot 3$ 的网格图 grid ,你需要用 红,黄,绿 三种颜色之一给每一个格子上色,且确保相邻格子颜色不同(也就是有相同水平边或者垂直边的格子颜色不同)。给你网格图的行数 $n$ 。请你返回给 grid 涂色的方案数。由于答案可能会非常大,请你返回答案对 $10^9 + 7$ 取余的结果。
解法:
- 简单dp,枚举每一行的所有状态,按行转移即可,时间复杂度$O(27n)$。
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
class Solution {
public:
long long dp[5005][30];
int numOfWays(int n) {
memset(dp,0,sizeof(dp));
// string up,now;
int up[3]={0},now[3]={0};
for(int i=0;i<=25;i++){
int x=i;
int cnt=0;
while(x){
up[cnt++]=x%3;
x/=3;
}
if(up[2]!=up[1]&&up[1]!=up[0]){
dp[1][i]=1;
}
}
for(int i=2;i<=n;i++){
for(int last=0;last<=25;last++){
for(int j=0;j<3;j++) up[j]=0;
bool ok=1;
int x=last;
int cnt=0;
while(x){
up[cnt++]=x%3;
x/=3;
}
if(up[2]!=up[1]&&up[1]!=up[0]){
for(int nxt=0;nxt<=25;nxt++){
for(int k=0;k<3;k++) now[k]=0;
int x=nxt;
int cnt=0;
while(x){
now[cnt++]=x%3;
x/=3;
}
if(now[2]!=now[1]&&now[1]!=now[0]){
if((now[0]!=up[0])&&(now[1]!=up[1])&&(now[2]!=up[2])){
dp[i][nxt]+=dp[i-1][last];
dp[i][nxt]%=1000000007;
}
}
}
}
}
}
long long ans=0;
for(int i=0;i<=25;i++){
ans+=dp[n][i];
ans%=1000000007;
}
return ans;
}
};
ABC162E. Sum of gcd of Tuples (Hard)
题意:
- 有一个未知序列$a$,长度为$n$,已知$a$中的每个元素都在$[1,k]$之内,这样的序列有$k^n$个,计算
解法:
- 考虑$cnt_{i}$为$gcd$为$i$的所有序列个数,那么$a$中的每个元素必定是$i$的倍数,这样的序列有${\lfloor \frac{k}{i} \rfloor}^n$个,但是这样我们也会把$gcd$为$2i,3i,4*i…$也会统计上,因此还得减去$cnt_{j}(j|i)$,我们从$k$到$1$倒着枚举即可,时间复杂度为$O(n \cdot \sqrt n)$。
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45/*
* @author: codancer
* @createTime: 2020-04-12, 21:02:02
*/
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
typedef vector<int> VI;
typedef vector<ll> VII;
typedef pair<int,int> pii;
ll qpow(ll a,ll b){
ll ans=1;
while(b){
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans%mod;
}
ll cnt[500005];
int main(){
ll n,k;
cin>>n>>k;
ll ans=0;
fep(i,k,1){
cnt[i]=qpow(k/i,n);
for(int j=2*i;j<=k;j+=i){
cnt[i]-=cnt[j];
cnt[i]+=mod;
cnt[i]%=mod;
}
ans+=((ll)i*cnt[i])%mod;
ans%=mod;
}
cout<<ans<<endl;
return 0;
}ABC162F. Select Half
题意:
- 从长度为$n$的数组$a$中选出$\lfloor \frac{n}{2} \rfloor$个不相邻的元素使他们和最大。
解法:
假设选择了前$i$个元素并且$a_{i}$被选择了,此时最少会有$\lfloor \frac{n+1}{2} \rfloor-1$个空格,类似于:
XOXOXOXO
XOXOXO(X是被选择的)
令$dp_{i,j}$代表选择了$a_{i}$并且比$\lfloor \frac{i+1}{2} \rfloor-1$多使用了$j$个空格,$j$最大为$2$,此时的最大值。
因此我们可以得出转移方程:
$dp_{i,0}=dp_{i-2,0}+a_{i}$ (一个也不多,那么肯定是选择了第$i-2$个,接下来就是$i$)
$dp_{i,1}=max(dp_{i-2,1},dp_{i-3,0})+a_{i}$(多一个空格,可能是从$i-2$之前就多了,或者多出来的就是$i-2$)
$dp_{i,2}=max(dp_{i-2,2},dp_{i-3,1},dp_{i-4,0})+a_{i}$(读者可以自己分析,不再赘述,和$dp_{i,1}$的情况类似)
转移完之后考虑$n$的奇偶性,如果$n$为奇数。
假设最后选了第$n$个,结果为$dp_{n,2}$(肯定多$2$个)
XOXOOOX(本来是最少用$3-1=2$个空格,现在用了$4$个)
假设最后选择了第$(n-1)$个,那么结果为$dp_{n-1,1}$(肯定多$ 1 $个)
XOXOOXO(本来是最少用$3-1=2$个空格,现在用了$ 3 $个)
假设最后选择了第$(n-2)$个,那么结果为$dp_{n-2,0}$
XOXOXOO(只用了两个)
偶数情况类似,答案取$max(dp_{n-1,0},dp_{n,1})$,读者可以自己讨论。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/*
* @author: codancer
* @createTime: 2020-04-13, 10:42:14
*/
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
typedef vector<int> VI;
typedef vector<ll> VII;
typedef pair<int,int> pii;
const int N = 2e5+100;
ll dp[N][3];
ll a[N];
int main(){
int n;
cin>>n;
rep(i,1,n){
cin>>a[i];
}
rep(i,1,n){
rep(j,0,2){
dp[i][j]=-1e18;
}
}
rep(i,1,3) dp[i][i-1]=a[i];
rep(i,3,n){
if(i>2){
dp[i][0]=max(dp[i][0],dp[i-2][0])+a[i];
dp[i][1]=max(dp[i][1],dp[i-2][1]+a[i]);
dp[i][2]=max(dp[i][2],dp[i-2][2]+a[i]);
}
if(i>3){
dp[i][1]=max(dp[i][1],dp[i-3][0]+a[i]);
dp[i][2]=max(dp[i][2],dp[i-3][1]+a[i]);
}
if(i>4){
dp[i][2]=max(dp[i][2],dp[i-4][0]+a[i]);
}
}
ll ans=-1e18;
if(n&1){
ans=max(ans,dp[n][2]);
if(n>1){
ans=max(ans,dp[n-1][1]);
}
if(n>2){
ans=max(ans,dp[n-2][0]);
}
}else{
ans=max(ans,dp[n][1]);
if(n>1) ans=max(ans,dp[n-1][0]);
}
cout<<ans<<endl;
return 0;
}