0%

AC代码

image

最主要的思想就是对前缀和进行一个预处理(即mod M)

1lrN((lirAi)modM)=1lrN(SrSl1)modM, \sum_{1 \leq l \leq r \leq N} \left( \left(\sum_{l \leq i \leq r} A_i\right) \mathbin{\mathrm{mod}} M \right) = \sum_{1 \leq l \leq r \leq N} (S_r - S_{l-1}) \mathbin{\mathrm{mod}} M,

再说的透一点,就是想怎么把这个对拍程序时间复杂度最高的这部分给化简

1
2
3
4
5
for(int i=1;i<=n;i++) {
for(int j=i;j>=1;j--) {
ans=ans+(sumA[i]-sumA[j-1])%m;
}
}

这题如果你比较老实,你可能需要使用平衡树,但是你永远可以相信vector大法(虽然好像有点极限)

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
#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <bitset>
#include <iterator>

using namespace std;
typedef long long ll;
const ll N=2e5+10;
ll n,m,A[N],dp[N],ans;
ll sumA[N]; // 经过mod M预处理的前缀和数组A
ll ss[N]; // sumA[]的前缀和(即前缀和的前缀和)
vector<ll> front;

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) {
cin>>A[i];
sumA[i]=(sumA[i-1]+A[i])%m;
}
for(int i=1;i<=n;i++) {
ss[i]=ss[i-1]+sumA[i];
}
for(int i=1;i<=n;i++) {
ll dis=0;
dis=upper_bound(front.begin(),front.end(),sumA[i])-front.begin()+1;
// m*(i-1-dis+1) i-1为multiset的长度,-dis后要+1,故抵消
ans=ans+(sumA[i]*i-(ss[i-1]-ss[0])+m*(i-dis));
front.insert(lower_bound(front.begin(),front.end(),sumA[i]),sumA[i]);
}
cout<<ans<<endl;
}

心路历程(WA,TLE,MLE……)

对拍程序

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
#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <bitset>

using namespace std;
typedef long long ll;
const ll N=2e5+10;
ll n,m,A[N],dp[N],sumA[N],ans;

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) {
cin>>A[i];
sumA[i]=sumA[i-1]+A[i];
}
for(int i=1;i<=n;i++) {
for(int j=i;j<=n;j++) {
ans=ans+(sumA[j]-sumA[i-1])%m;
}
}
cout<<ans<<endl;
}

distance 常数太大了

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
#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <bitset>
#include <iterator>

using namespace std;
typedef long long ll;
const ll N=2e5+10;
ll n,m,A[N],dp[N],ans;
ll sumA[N]; // 经过mod M预处理的前缀和数组A
ll ss[N]; // sumA[]的前缀和(即前缀和的前缀和)
multiset<ll> front;

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) {
cin>>A[i];
sumA[i]=(sumA[i-1]+A[i])%m;
}
for(int i=1;i<=n;i++) {
ss[i]=ss[i-1]+sumA[i];
}
for(int i=1;i<=n;i++) {
ll dis=0;
dis=distance(front.begin(),front.upper_bound(sumA[i]))+1;
// m*(i-1-dis+1) i-1为multiset的长度,-dis后要+1,故抵消
ans=ans+(sumA[i]*i-(ss[i-1]-ss[0])+m*(i-dis));
front.insert(sumA[i]);
}
cout<<ans<<endl;
}

AC代码

心路历程(WA,TLE,MLE……)

AC代码

中心思想就是
// 以i点为中间点,对全部路径进行更新

只是求个最短路径的话连Path数组都不需要

AC记录

https://www.luogu.com.cn/record/186274091

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
#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <bitset>

using namespace std;
typedef long long ll;
const ll N=110;
ll n,m,D[N][N],inf; // P[N][N];

void debug() {
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++)
cout<<D[i][j]<<" ";
cout<<endl;
}
}

inline void Floyd() {
for(int i=1;i<=n;i++) { // 以i点为中间点,对全部路径进行更新
for(int j=1;j<=n;j++) {
if(j==i) // 1->xxx node 无需更新
continue;
for(int k=1;k<=n;k++) {
if(k==i)
continue;
// 以i点为中间点,
D[j][k]=min(D[j][k],D[j][i]+D[i][k]);
}
}
}
debug();
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
memset(D,0x3f,sizeof(D)); // 初始化
inf=D[0][0];
for(int i=1;i<=n;i++)
D[i][i]=0;
for(int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
P[i][j]=-1;
for(int i=1;i<=m;i++) {
ll u,v,w;
cin>>u>>v>>w;
D[u][v]=min(D[u][v],w); // 处理一下重边与自环等特殊情况
D[v][u]=min(D[v][u],w);
// P[u][v]=u; // u->v的最短路径就是u->v,存最后一个点的前驱,即u
}
// debug();
// cout<<endl;
Floyd();
}
// AC https://www.luogu.com.cn/record/186274091

心路历程(WA,TLE,MLE……)

AC代码

时光倒流trick+floyd

注意重边处理

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <bitset>

using namespace std;
typedef long long ll;
const ll N=310,M=90100;
ll n,m,query,cnt,inf; // D里存的是到该点的最短距离(目前)
ll g[N][N],C[M];
pair<ll,ll> road[M];
vector<ll> ans; // 逆序存储答案
struct opxy {
ll op,x,y;
};
vector<opxy> opp; // 就是存一下操作,实现时光倒流

// 时光倒流trick+floyd(以每个点为中间点进行的距离dp)

inline void floyd() {
for(int i=1;i<=n;++i) {
for(int j=1;j<=n;++j) {
if(i==j)
continue;
for(int k=1;k<=n;k++) {
if(k==i)
continue;
g[j][k]=min(g[j][i]+g[i][k],g[j][k]);
}
}
}
}

inline void update(ll a,ll b) {
ll i=a;
for(int j=1;j<=n;++j) {
if(i==j)
continue;
for(int k=1;k<=n;k++) {
if(k==i)
continue;
g[j][k]=min(g[j][i]+g[i][k],g[j][k]);
}
}
i=b;
for(int j=1;j<=n;++j) { // (与上一段重复)
if(i==j)
continue;
for(int k=1;k<=n;k++) {
if(k==i)
continue;
g[j][k]=min(g[j][i]+g[i][k],g[j][k]);
}
}
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m>>query;
memset(g,0x3f,sizeof(g)); // g初始化
inf=g[0][0];
for(int i=1;i<=n;++i) {
g[i][i]=0; // 自己到自己肯定距离为0
}
for(int i=1;i<=m;++i) {
ll a,b,c;
cin>>a>>b>>c;
g[a][b]=min(c,g[a][b]);
g[b][a]=min(c,g[b][a]);
road[++cnt]=make_pair(a,b);
C[cnt]=c;
}
for(int _=1;_<=query;++_) {
int op;
cin>>op;
if(op==1) {
ll idx;
cin>>idx;
ll a,b;
a=road[idx].first,b=road[idx].second;
if(g[a][b]==C[idx])
g[a][b]=inf,g[b][a]=inf; // 删边(处理重边情况)
opxy lop; lop.op=1,lop.x=idx;
opp.push_back(lop);
}else {
ll x,y;
cin>>x>>y;
opxy lop; lop.op=op,lop.x=x,lop.y=y;
opp.push_back(lop);
}
}
floyd();
for(int i=opp.size()-1;i>=0;--i) { // 哈哈,时光倒流启动!!!
ll op=opp[i].op,x=opp[i].x,y=opp[i].y;
if(op==1) {
ll a=road[x].first,b=road[x].second;
g[a][b]=min(C[x],g[a][b]),g[b][a]=min(C[x],g[b][a]);
update(a,b);
}else {
if(g[x][y]!=inf)
ans.push_back(g[x][y]);
else
ans.push_back(-1);
}
}
for(int i=ans.size()-1;i>=0;--i) {
cout<<ans[i]<<endl;
}
}
//不知道为什么RE了好几个 https://atcoder.jp/contests/abc375/submissions/59448981
// WA 未处理重边 https://atcoder.jp/contests/abc375/submissions/59449001
// 处理了一下重边 AC https://atcoder.jp/contests/abc375/submissions/59449193

心路历程(WA,TLE,MLE……)

能跑,但是TLE,我发现这题竟然是floyd算法

需要时光倒流trick+floyd

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
#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <bitset>

using namespace std;
typedef long long ll;
const ll N=310,M=9010;
ll n,m,query,cnt,D[N],inf; // D里存的是到该点的最短距离(目前)
vector<pair<ll,ll> > g[N];
pair<ll,ll> road[9010];
bool ban[N][N];
bitset<N> done; // 存的就是dij当中已经确定的点
struct cmp {
bool operator()(pair<ll,ll> a,pair<ll,ll> b) {
if(a.second!=b.second)
return a.second>b.second; // 我们需要一个小根堆,second是存的边的长度
return false;
}
};
inline ll solve(ll x,ll y) {
priority_queue<pair<ll,ll>,vector<pair<ll,ll> >, cmp> q;
D[x]=0;
q.push(make_pair(x,0));
while (!q.empty()) {
ll cdis=q.top().second,cn=q.top().first;
q.pop();
if(done[cn]) // 这种写法是可能会有冗余,故要判断一下
continue;
done[cn]=true;
D[cn]=cdis;
if(done[y])
return D[y];
for(int i=0;i<g[cn].size();i++) {
ll fn=g[cn][i].first,fdis=g[cn][i].second+cdis;
if(ban[cn][fn])
continue;
q.push(make_pair(fn,fdis));
}
}
if(D[y]==inf)
return -1;
return D[y];
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m>>query;
for(int i=1;i<=m;i++) {
ll a,b,c;
cin>>a>>b>>c;
g[a].push_back(make_pair(b,c));
g[b].push_back(make_pair(a,c));
road[++cnt]=make_pair(a,b);
}
for(int _=1;_<=query;_++) {
int op;
cin>>op;
if(op==1) {
ll idx;
cin>>idx;
ll a=road[idx].first,b=road[idx].second;
ban[a][b]=true;ban[b][a]=true;
}else {
ll x,y;
cin>>x>>y;
memset(D,0x3f,sizeof(D));
inf=D[0];
done.reset();
cout<<solve(x,y)<<endl;
}
}
}

WA了,可能是没有判别重边

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <bitset>

using namespace std;
typedef long long ll;
const ll N=310,M=90100;
ll n,m,query,cnt,inf; // D里存的是到该点的最短距离(目前)
ll g[N][N],C[M];
pair<ll,ll> road[M];
vector<ll> ans; // 逆序存储答案
struct opxy {
ll op,x,y;
};
vector<opxy> opp; // 就是存一下操作,实现时光倒流

// 时光倒流trick+floyd(以每个点为中间点进行的距离dp)

inline void floyd() {
for(int i=1;i<=n;++i) {
for(int j=1;j<=n;++j) {
if(i==j)
continue;
for(int k=1;k<=n;k++) {
if(k==i)
continue;
g[j][k]=min(g[j][i]+g[i][k],g[j][k]);
}
}
}
}

inline void update(ll a,ll b) {
ll i=a;
for(int j=1;j<=n;++j) {
if(i==j)
continue;
for(int k=1;k<=n;k++) {
if(k==i)
continue;
g[j][k]=min(g[j][i]+g[i][k],g[j][k]);
}
}
i=b;
for(int j=1;j<=n;++j) { // (与上一段重复)
if(i==j)
continue;
for(int k=1;k<=n;k++) {
if(k==i)
continue;
g[j][k]=min(g[j][i]+g[i][k],g[j][k]);
}
}
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m>>query;
memset(g,0x3f,sizeof(g)); // g初始化
inf=g[0][0];
for(int i=1;i<=n;++i) {
g[i][i]=0; // 自己到自己肯定距离为0
}
for(int i=1;i<=m;++i) {
ll a,b,c;
cin>>a>>b>>c;
g[a][b]=c;
g[b][a]=c;
road[++cnt]=make_pair(a,b);
C[cnt]=c;
}
for(int _=1;_<=query;++_) {
int op;
cin>>op;
if(op==1) {
ll idx;
cin>>idx;
ll a,b;
a=road[idx].first,b=road[idx].second;
g[a][b]=inf,g[b][a]=inf; // 删边
opxy lop; lop.op=1,lop.x=idx;
opp.push_back(lop);
}else {
ll x,y;
cin>>x>>y;
opxy lop; lop.op=op,lop.x=x,lop.y=y;
opp.push_back(lop);
}
}
floyd();
for(int i=opp.size()-1;i>=0;--i) { // 哈哈,时光倒流启动!!!
ll op=opp[i].op,x=opp[i].x,y=opp[i].y;
if(op==1) {
ll a=road[x].first,b=road[x].second;
g[a][b]=C[x],g[b][a]=C[x];
update(a,b);
}else {
if(g[x][y]!=inf)
ans.push_back(g[x][y]);
else
ans.push_back(-1);
}
}
for(int i=ans.size()-1;i>=0;--i) {
cout<<ans[i]<<endl;
}
}
//不知道为什么RE了好几个 https://atcoder.jp/contests/abc375/submissions/59448981

AC代码

这题题意很简单,把一段序列分为若干个子序列,要求这些子序列的和≠k,统计这种子序列的分法,mod 998244353

样例的计算过程如下

3 3
1 2 3

问分法种数,即2种

推出思路是这样的,不断枚举末尾

image

O(n^2)优化嘛就是用map维护之前的j
// 我们需要sumA[j]!=sumA[i]-k; 那为何不反过来想,把==的排除掉不就好了吗?

主要思路如上

AC

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
#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <bitset>

using namespace std;
typedef long long ll;
const ll N=2e5+10,mod=998244353;
ll n,k,A[N],sumA[N],dp[N]; // dp[i]的意思是以i为结尾时,有多少种分法可行
map<ll,ll> occSum; // occSum[i] i表示对应sum值,occSum[i]表示该sum值累计方案数 occ即occurred简写
ll currSum; // i之前的现在的方案数总和
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n;++i) {
cin>>A[i];
sumA[i]=sumA[i-1]+A[i];
}
dp[0]=1;
occSum[0]=1;
currSum+=dp[0];
for(int i=1;i<=n;++i) {
// 我们需要sumA[j]!=sumA[i]-k;那为何不反过来想,把==的排除掉不就好了吗?
if(occSum.count(sumA[i]-k))
dp[i]=(dp[i]%mod+currSum%mod-occSum[sumA[i]-k]%mod+mod)%mod;
else
dp[i]=currSum%mod;
occSum[sumA[i]]=(occSum[sumA[i]]%mod+dp[i]%mod+mod)%mod;
currSum=(currSum%mod+dp[i]%mod+mod)%mod;
// for(int j=i-1;j>=0;--j) {
// if(sumA[i]-sumA[j]!=k)
// dp[i]=(dp[i]%mod+dp[j]%mod)%mod;
// }
}
cout<<dp[n]<<endl;
}
// 哈哈,第一次提交忘记mod了 https://atcoder.jp/contests/abc370/submissions/59317169
// AC https://atcoder.jp/contests/abc370/submissions/59320083

心路历程(WA,TLE,MLE……)

如果发现WA的比较多,但是又觉得没啥问题,可能是没mod的问题

WA,原因就是C++的%是取余,而题目要求取模,故要在%的时候加一个mod。

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
#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <bitset>

using namespace std;
typedef long long ll;
const ll N=2e5+10,mod=998244353;
ll n,k,A[N],sumA[N],dp[N]; // dp[i]的意思是以i为结尾时,有多少种分法可行
map<ll,ll> occSum; // occSum[i] i表示对应sum值,occSum[i]表示该sum值累计方案数 occ即occurred简写
ll currSum; // i之前的现在的方案数总和
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n;++i) {
cin>>A[i];
sumA[i]=sumA[i-1]+A[i];
}
dp[0]=1;
occSum[0]=1;
currSum+=dp[0];
for(int i=1;i<=n;++i) {
// 我们需要sumA[j]!=sumA[i]-k;那为何不反过来想,把==的排除掉不就好了吗?
if(occSum.count(sumA[i]-k))
dp[i]=(dp[i]+currSum-occSum[sumA[i]-k])%mod;
else
dp[i]=currSum%mod;
occSum[sumA[i]]=(occSum[sumA[i]]+dp[i])%mod;
currSum=(currSum+dp[i])%mod;
// for(int j=i-1;j>=0;--j) {
// if(sumA[i]-sumA[j]!=k)
// dp[i]=(dp[i]%mod+dp[j]%mod)%mod;
// }
}
cout<<dp[n]<<endl;
}
// 哈哈,第一次提交忘记mod了 https://atcoder.jp/contests/abc370/submissions/59317169