Codeforces Round #753 (Div. 3) F-H

F. Robot on the Board 2(搜索)

题意:

题意:有一个n*m的棋盘,每个格子上有一个字符,U, D, L, R四个之一。机器人从任意一个格子为起始位置,按着字符方向(U,D,L,R分别是上下左右)走一格,直到碰到走过的格子或者出边界,最长的运动路径长度是多少。输出使运动路径最长的起始位置和路径长度

题解:

遍历每个点并对每个点按照规则搜索,同时记录走过的路径,走过的路径无需重复搜索。同时要注意如果搜索的路径有环,那么要把有环的地方的$len[x][y]$标记为同样的大小。每个点都只会最多被访问两次,复杂度O(n*m)。

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
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")//手动开栈
#include<bits/stdc++.h>
#define pii pair<int,int>
#define pdd pair<double,double>
using namespace std;
typedef long long ll;
const int MAXN = 1e6+10;
const int INF = 0x3f3f3f3f;
const ll MOD = 998244353;


int n,m;
char a[2000+2][2000+2];
int len[2000+2][2000+2];
int vis[2000+2][2000+2];
vector<pii>t;

bool is_cir = false;
bool check(int x,int y)
{
if(vis[x][y]==1) {is_cir=true;return false;}
if(vis[x][y]==1||x<=0||y<=0||x>n||y>m) return false;
else return true;
}

void dfs(int x,int y)
{
is_cir = false;
t.clear();
while(check(x,y))
{
// cout<<x<<" "<<y<<endl;
t.push_back({x,y});
vis[x][y] = 1;
if(a[x][y]=='U') x--;
else if(a[x][y]=='D') x++;
else if(a[x][y]=='L') y--;
else if(a[x][y]=='R') y++;
}
int xx=x, yy=y;
int cnt = len[x][y];
for(int i=t.size()-1;i>=0;i--)
{
cnt++;
len[t[i].first][t[i].second] = cnt;
if(is_cir&&t[i].first==xx&&t[i].second==yy)
{
for(int j=i;j<t.size();j++) len[t[j].first][t[j].second] = cnt;
is_cir = false;
}
}
}


int main()
{
ios::sync_with_stdio(false);
cin.tie(0);

int T;
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=0;i<=n+2;i++)
{
for(int j=0;j<=m+2;j++)
{
len[i][j] = 0;
vis[i][j] = 0;
}
}
for(int i=1;i<=n;i++)
{
string s;
cin>>s;
for(int j=1;j<=m;j++)
{
a[i][j] = s[j-1];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
dfs(i,j);
}
}
int maxx = -1;
pii ans;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(len[i][j]>maxx)
{
maxx = len[i][j];
ans.first = i;
ans.second = j;
}
}
}
cout<<ans.first<<" "<<ans.second<<" "<<maxx<<endl;
}
return 0;
}

/*
ios::sync_with_stdio(false);
cin.tie(0);
*/

G. Banquet Preparations 1(构造,贪心)

题意:

​ 给n组数,每组包含$ai,b_i$两个数,现在可以对每组数做减去操作,操作是:使a减去一个非负数x,使b减去一个非负数y,同时保证x+y=m。希望每个数操作后使得$|\Sigma{i=1}^nai-\Sigma{i=1}^nb_i|$最小,输出每组数的a和b分别多少。

题解:

​ 一开始的这些数总共大小是$\Sigma{i=1}^na_i+\Sigma{i=1}^nbi$,需要减去的大小是$n*m$,那么操作完后这些数的总共大小是$left = \Sigma{i=1}^nai+\Sigma{i=1}^nb_i-n*m$, 我们希望剩下得$a,b$绝对值差最小,即进行完减去操作后,a和b两边的和各是$left/2$。

​ 我们可以求出$a_i$进行完减去操作后的范围$[l,r]$, 只需要构造出$a_i$的和尽量接近$left/2$即可。

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
#include<bits/stdc++.h>
#define pii pair<int,int>
#define pdd pair<double,double>
using namespace std;
typedef long long ll;
const int MAXN = 1e6+10;
const int INF = 0x3f3f3f3f;
const ll MOD = 998244353;

pii a[MAXN];
pii rg[MAXN];
int ler[MAXN];
pii ori[MAXN];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);

int T;
cin>>T;
while(T--)
{
int n,m;
cin>>n>>m;
ll sum = 0;
for(int i=1;i<=n;i++)
{
cin>>a[i].first>>a[i].second;
ori[i].first = a[i].first;
ori[i].second = a[i].second;
sum += 1ll*a[i].first + 1ll*a[i].second;
ll l = max(0,m-a[i].second);
ll r = min(m,a[i].first);
rg[i].first = a[i].first - r;
rg[i].second = a[i].first - l;
}
sum = sum - 1ll*n*m;
sum/=2;
ll lef = sum;
for(int i=1;i<=n;i++)
{
lef-=rg[i].first;
}
if(lef>0)
{
for(int i=1;i<=n;i++)
{
a[i].first -= rg[i].first;
ler[i] = rg[i].second - rg[i].first;
}
for(int i=1;i<=n;i++)
{
if(lef>=ler[i])
{
a[i].first -= ler[i];
lef -= ler[i];
}
else
{
a[i].first -= lef;
lef-=lef;
}
}
ll ans = 0;
for(int i=1;i<=n;i++)
{
ans+=1ll*(ori[i].first-a[i].first)-1ll*(ori[i].second-(m-a[i].first));
}
cout<<abs(ans)<<endl;
for(int i=1;i<=n;i++)
{
cout<<a[i].first<<" "<<m-a[i].first<<endl;
}
}
else
{
ll ans = 0;
for(int i=1;i<=n;i++)
{
a[i].first -= rg[i].first;
a[i].second =m - a[i].first;
ans+=1ll*(ori[i].first-a[i].first)-1ll*(ori[i].second-a[i].second);
}
cout<<abs(ans)<<endl;
for(int i=1;i<=n;i++)
{
cout<<a[i].first<<" "<<a[i].second<<endl;
}
}
}

return 0;
}

H. Banquet Preparations 2(双指针,区间合并)

题意:

$n$个非负三元组$a_i, b_i, m_i$,保证$a_i + b_i ≥ m_i$ 。让每个三元组$a_i$ , $b_i$总共减少$m_i$。问最后使得减少后的$(a_i,b_i)$对 不同的对数最少的方案,和对数。

题解:

当一组数做完减去操作后,他们剩下的和是$sum=a_i+b_i-m_i$,只要当剩下的和$sum$相同才有可能$a,b$分别相同,所以我们先按照$sum$作为第一关键字进行排序。

接下来我们求出每个$a$的范围,如果剩下的$sum$相同且$a$的范围有交集,那么两组数就可以使得$(a,b)$相同。然后我们把a的范围$[range_l,range_r]$的$range_l$作为第二关键字,$range_r$作为第三关键字,最后对他们用双指针做区间合并,如果无交集区间则ans++统计答案。排序复杂度$O(nlogn)$,双指针区间合并$O(n)$,总复杂度$O(nlogn)$。

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
////
#include<bits/stdc++.h>
#define pii pair<int,int>
#define pdd pair<double,double>
using namespace std;
typedef long long ll;
const int MAXN = 1e6+10;
const int INF = 0x3f3f3f3f;
const ll MOD = 998244353;

struct node{
int a,b,m;
int sum=0;
int rgl,rgr;
int newa,newb;
int id;
}p[MAXN];

bool cmp(node a,node b)
{
if(a.sum==b.sum)
{
if(a.rgl==b.rgl) return a.rgr<b.rgr;
else return a.rgl<b.rgl;
}
else return a.sum<b.sum;
}

bool cmp2(node a,node b)
{
return a.id<b.id;
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0);

int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>p[i].a>>p[i].b>>p[i].m;
p[i].id = i;
p[i].rgl = max(0,p[i].a-p[i].m);
p[i].rgr = min(p[i].a,p[i].a-(p[i].m-p[i].b));
p[i].sum = p[i].a + p[i].b - p[i].m;
}
sort(p+1,p+1+n,cmp);
int ans = 0;
int l = p[1].rgl,r = p[1].rgr;
int last_i = 1;
for(int i=2;i<=n;i++)
{
if(p[i].sum==p[i-1].sum)
{
int nowl = p[i].rgl, nowr = p[i].rgr;
if(nowl<=r)
{
l = nowl;
r = min(r,nowr);
}
else
{
for(int j=last_i;j<=i-1;j++)
{
p[j].newa = l;
p[j].newb = p[j].b - (p[j].m-(p[j].a-l));
}
ans++;
l = p[i].rgl;
r = p[i].rgr;
last_i = i;
}
}
else
{
for(int j=last_i;j<=i-1;j++)
{
p[j].newa = l;
p[j].newb = p[j].b - (p[j].m-(p[j].a-l));
}
ans++;
l = p[i].rgl;
r = p[i].rgr;
last_i = i;
}
}
for(int j=last_i;j<=n;j++)
{
p[j].newa = l;
p[j].newb = p[j].b - (p[j].m-(p[j].a-l));
}
ans++;
cout<<ans<<endl;
sort(p+1,p+1+n,cmp2);
for(int i=1;i<=n;i++)
{
cout<<p[i].a-p[i].newa<<" "<<p[i].b-p[i].newb<<endl;
}
}
return 0;
}