JOISC 2020 Day3
T1 Constellation3
原题
CSDN下载:https://download.csdn.net/download/Ljnoit/12262096
链接
翻译
题目描述
JOI 君拍了一张 N × N N×N N×N 的星空图,将左起第 X X X 列,下起第 Y Y Y 行的像素点称为像素 ( X , Y ) (X,Y) (X,Y)。
画面里有白色的大楼,黄色的星星,黑色的空格。第 i i i 列从最下方到自下数起第 A i A_i Ai 行都是白色的大楼。有 M M M 个星星,第 j j j 个星星位于像素点 ( X j , Y j ) (X_j,Y_j) (Xj,Yj)。此外,所有的像素点都是黑色。
若一个长方形区域可以称作星座,则满足以下条件:
- 1.不含白色像素点。
- 2.至少存在两个星星。
看厌了星座的 JOI 君要把一些黄色的星星涂成黑色,使得没有星座存在。将第 j j j 个星星涂成黑色会使照片的不自然度增加 C j C_j Cj,最初不自然度为 0 0 0。求不自然度的最小值。
输入格式
输入第一行为一个整数 N N N,表示地图的边长大小。
第二行为 N N N个整数 A 1 , … , A N A_1,…,A_N A1,…,AN,描述如题目。
第三行为一个整数 M M M,表示星星的个数。
接下来的 M M M 行,每行三个整数 X i , Y i , C i X_i,Y_i,C_i Xi,Yi,Ci,即对第 i i i 个星星的描述。
输出格式
输出不自然度的最小值。
样例输入 1
5
1 3 4 2 3
3
1 5 3
4 3 2
2 4 2
样例输出 1
2
样例解释 1
可以发现把第三个星删了之后它就和一号构不成星座,且比删去一号花费少。
样例输入 2
7
5 6 2 3 6 7 6
5
7 7 5
3 3 7
3 7 10
1 7 6
4 7 8
样例输出 2
16
样例解释 2
删去三号和四号。
数据范围
对于 100 100 100% 的数据, 1 ≤ N , M ≤ 200000 1≤N,M≤200000 1≤N,M≤200000,保证:
- 1 ≤ A i ≤ N ( 1 ≤ i ≤ N ) 1≤A_i≤N(1≤i≤N) 1≤Ai≤N(1≤i≤N);
- 1 ≤ X j , Y j ≤ N ( 1 ≤ j ≤ M ) 1≤X_j,Y_j≤N(1≤j≤M) 1≤Xj,Yj≤N(1≤j≤M);
- 1 ≤ C j ≤ 1 0 9 ( 1 ≤ j ≤ M ) 1≤C_j≤10^9(1≤j≤M) 1≤Cj≤109(1≤j≤M);
- A X j < Y j ( 1 ≤ j ≤ M ) A_{X_j}<Y_j(1≤j≤M) AXj<Yj(1≤j≤M);
- ( X j , Y j ) ≠ ( X k , Y k ) ( 1 ≤ j < k ≤ M ) (X_j,Y_j)≠(X_k,Y_k)(1≤j<k≤M) (Xj,Yj)=(Xk,Yk)(1≤j<k≤M)。
详细子任务及附加限制如下表:
子任务编号 | 附加限制 | 分值 |
---|---|---|
1 | N , M ≤ 300 N,M≤300 N,M≤300 | 14 |
2 | N , M ≤ 2000 N,M≤2000 N,M≤2000 | 21 |
3 | 无附加限制 | 65 |
解析
法一:
笛卡尔树+线段树合并
对于一个区间[li,ri],找到最高点a[x]。
这个区间只能有一个
>
=
a
[
x
]
>=a[x]
>=a[x]的点。
暴力就是先建出笛卡尔树:
设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri]里选的最高点是j的最大和。
转移有:
x
x
x这里选一个点
j
0
j_0
j0,左区间选一个
j
1
j_1
j1,右区间选一个
j
2
j_2
j2。
可以一个接一个合并,复杂度
O
(
n
2
)
O(n^2)
O(n2)。
用线段树合并优化一下就O(
n
l
o
g
n
n log n
nlogn)
法二:
笛卡尔树+树状数组+dfs序
考虑把笛卡尔树建成一个树形关系。
如果在
x
x
x这里选了一个
y
y
y,选
y
y
y就相当于覆盖了
x
x
x到
x
x
x的一个祖先
z
z
z(
a
[
z
]
<
=
y
a[z]<=y
a[z]<=y且
a
[
f
a
[
z
]
]
>
y
a[fa[z]]>y
a[fa[z]]>y)。
可以倍增求出
z
z
z。
问题变为一棵树上有若干祖先后代链,选出不相交的若干链,使权值和最大。
经典问题(不是祖先后代链也能做),只需要dfs序+树状数组实时维护一个点到根的dp值的和就差不多了。
代码
#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#define R register int
#define re(i,a,b) for(R i=a; i<=b; i++)
#define ms(i,a) memset(a,i,sizeof(a))
#define MAX(a,b) (((a)>(b)) ? (a):(b))
#define MIN(a,b) (((a)<(b)) ? (a):(b))
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
namespace IO {
#include <cctype>
template <typename T>
inline void read(T &x){
x=0;
char c=0;
T w=0;
while (!isdigit(c)) w|=c=='-',c=getchar();
while (isdigit(c)) x=x*10+(c^48),c=getchar();
if(w) x=-x;
}
template <typename T>
inline void write(T x) {
if(x<0) putchar('-'),x=-x;
if(x<10) putchar(x+'0');
else write(x/10),putchar(x%10+'0');
}
template <typename T>
inline void writeln(T x) {
write(x);
putchar('\n');
}
}
using IO::read;
using IO::write;
using IO::writeln;
const int N=2e5+5;
struct Node {
int f[N];
int getfa(int x) {
return x==f[x] ? x : f[x]=getfa(f[x]);
}
void init(int n) {
for(int i=1; i<=n+1; i++) f[i]=i;
}
void merge(int x,int y) {
int fx=getfa(x),fy=getfa(y);
if(fx!=fy) f[fx]=fy;
}
} f1,f2;
int n,m;
LL a[N],c[N];
vector <int> v[N];
vector <PII> s[N];
inline void add(int x,LL g) {
while(x<=n+1) {
c[x]+=g;
x+=(x&(-x));
}
}
inline LL ask(int x) {
LL ret=0;
while(x) {
ret+=c[x];
x-=(x&(-x));
}
return ret;
}
int main() {
read(n);
f1.init(n);
f2.init(n);
for(int i=1; i<=n; i++) {
read(a[i]);
v[a[i]].push_back(i);
}
read(m);
int x,y,z;
for(int i=1; i<=m; i++) {
read(x); read(y); read(z);
s[y].push_back(make_pair(x,z));
}
LL ans=0;
for(int i=1; i<=n; i++) {
for(auto r:s[i]) {
LL tmp=ask(r.first);
if(r.second>=tmp){
ans+=tmp;
add(f1.getfa(r.first)+1,r.second-tmp);
add(f2.getfa(r.first),-r.second+tmp);
}
else ans+=r.second;
}
for(auto r:v[i]) {
f1.merge(r,r-1);
f2.merge(r,r+1);
}
}
writeln(ans);
return 0;
}
T2 Harvest
原题
CSDN下载:https://download.csdn.net/download/Ljnoit/12262096
链接
翻译
题目描述
IOI 庄园有 N N N 个员工, M M M 棵苹果树种在湖岸。湖的周长为 L L L 米。
一开始员工 i i i 位于从湖的最北端向顺时针方向前进 A i A_i Ai 米处,所有 A i A_i Ai 互异。苹果树 j j j 生长在从湖的最北端向顺时针方向前进 B j B_j Bj 米处,所有 B j B_j Bj 互异。
每棵苹果树最多长一个苹果,收获后 C C C 秒会长出一个新的。时刻 0 0 0 时,所有的苹果树上都有一个苹果。员工从时刻 0 0 0 开始从各自的地点以 1 m / s 1m/s 1m/s 的速度顺时针前进,遇到成熟的苹果就将其摘下(若到达时刚长出苹果,也要摘下),摘苹果的时间忽略不计。
现给出 Q Q Q 个询问,第 k k k 次询问员工 V k V_k Vk 在时刻 T k T_k Tk 结束时一共收获到几个苹果。
输入格式
输入第一行为四个整数 N , M , L , C N,M,L,C N,M,L,C,意义由题面所示。
第二行为 N N N 个整数 A 1 , … , A N A_1,…,A_N A1,…,AN。
第三行为 M M M 个整数 B 1 , … , B M B_1,…,B_M B1,…,BM。
第四行为一个整数 Q Q Q,即询问的数量。
接下来的 Q Q Q 行,每行两个整数 V i , T i V_i,T_i Vi,Ti。
输出格式
输出共 Q Q Q 行,第 k k k 行输出一个整数为第 k k k 个问题的答案。
样例输入 1
3 2 7 3
1 4 6
0 5
3
1 7
2 3
3 8
样例输出 1
2
1
1
样例解释 1
在第 1 个时刻,员工 2 从第 2 棵苹果树上收获了苹果,员工 3 从第 1 棵苹果树上收获了苹果。
在第 3 个时刻,员工 2 到达了第 1 棵苹果树。但是因为那时树上没果子所以没收获。
在第 4 个时刻,员工 1 从第 2 棵苹果树上收获了苹果。
在第 6 个时刻,员工 1 从第 1 棵苹果树上收获了苹果,员工 3 到达了第 2 棵苹果树,但还是因为那时树上没果子所以没收获。
在第 8 个时刻,员工 2 从第 2 棵苹果树上收获了苹果,员工 3 到达了第 1 棵苹果树,但再一次因为那时树上没果子所以没收获。
到第 7 个时刻为止,员工 1 收获了 2 个苹果,故第一行输出 2。
样例输入 2
5 3 20 6
0 4 8 12 16
2 11 14
9
4 1932
2 93787
1 89
5 98124798
1 2684
1 137598
3 2
3 8375
4 237
样例输出 2
146
7035
7
7359360
202
10320
0
628
18
样例输入 3
8 15 217 33608
0 12 71 96 111 128 152 206
4 34 42 67 76 81 85 104 110 117 122 148 166 170 212
14
2 223544052420046341
3 86357593875941375
4 892813012303440034
1 517156961659770735
7 415536186438473633
6 322175014520330760
7 557706040951533058
6 640041274241532527
5 286263974600593111
8 349405886653104871
1 987277313830536091
5 989137777159975413
2 50689028127994215
7 445686748471896881
### 样例输出 3
33230868503053
3
5
1
123542793648997
8
165811220737767
8
7
1
1
7
7535161012043
132506837660717
数据范围
对于 100% 的数据, 1 ≤ N , M ≤ 2 × 1 0 5 1≤N,M≤2×10^5 1≤N,M≤2×105, N + M ≤ L ≤ 1 0 9 N+M≤L≤10^9 N+M≤L≤109, 1 ≤ C ≤ 1 0 9 1≤C≤10^9 1≤C≤109, 1 ≤ Q ≤ 2 × 1 0 5 1≤Q≤2×10^5 1≤Q≤2×105,保证:
- 0 ≤ A i < L ( 1 ≤ i ≤ N ) 0≤A_i<L(1≤i≤N) 0≤Ai<L(1≤i≤N);
- A i < A i + 1 ( 1 ≤ i ≤ N − 1 ) A_i<A_i+1(1≤i≤N−1) Ai<Ai+1(1≤i≤N−1);
- 0 ≤ B j < L ( 1 ≤ j ≤ M ) 0≤B_j<L(1≤j≤M) 0≤Bj<L(1≤j≤M);
- B j < B j + 1 ( 1 ≤ j ≤ M − 1 ) B_j<B_j+1(1≤j≤M−1) Bj<Bj+1(1≤j≤M−1);
- A i ≠ B j ( 1 ≤ i ≤ N , 1 ≤ j ≤ M ) A_i≠B_j(1≤i≤N,1≤j≤M) Ai=Bj(1≤i≤N,1≤j≤M);
- 1 ≤ V k ≤ N ( 1 ≤ k ≤ Q ) 1≤V_k≤N(1≤k≤Q) 1≤Vk≤N(1≤k≤Q);
- 1 ≤ T k ≤ 1000000000000000000 = 1 0 18 ( 1 ≤ k ≤ Q ) 1≤T_k≤1000000000000000000=10^{18}(1≤k≤Q) 1≤Tk≤1000000000000000000=1018(1≤k≤Q)。
详细子任务及附加限制如下表:
子任务编号 | 附加限制 | 分值 |
---|---|---|
1 | N , M , Q ≤ 3000 N,M,Q≤3000 N,M,Q≤3000 | 5 |
2 | T k ≥ 1015 T_k≥1015 Tk≥1015 | 20 |
3 | 无附加限制 | 75 |
解析
方法:基环树
考虑对每一个点求出fa[i]表示i取了一个果子后,下一个取这个果子的是谁。
因为每个点一条出边,所以是个基环内向树。
再处理一下每个果子第一次被谁吃,然后把每个果子的时间改为被第一个人吃的时间+到树根的时间。
用个线段树合并再查询一下得到树上的答案。
接着考虑环上的答案。
先破环为链,记一个前缀和数组s[x]表示从x走到1的答案,设环长是len。
x,y都是环上的点:
假设y子树里的一个果子到y的时间是T1,而x处有一个询问T2。
设
v
1
=
T
2
−
s
[
x
]
−
[
x
>
y
]
∗
l
e
n
v_1=T_2−s[x]−[x>y]*len
v1=T2−s[x]−[x>y]∗len
v
2
=
T
1
−
s
[
y
]
v_2=T_1−s[y]
v2=T1−s[y]
那么贡献就是:
[
v
1
>
=
v
2
]
∗
(
⌊
(
v
1
−
v
2
)
l
e
n
⌋
+
1
)
[v1>=v2]∗(⌊(v1−v2)len⌋+1)
[v1>=v2]∗(⌊(v1−v2)len⌋+1)
代码
#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#define RI register int
#define re(i,a,b) for(RI i=a; i<=b; i++)
#define ms(i,a) memset(a,i,sizeof(a))
#define MAX(a,b) (((a)>(b)) ? (a):(b))
#define MIN(a,b) (((a)<(b)) ? (a):(b))
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
namespace IO {
#include <cctype>
template <typename T>
inline void read(T &x){
x=0;
char c=0;
T w=0;
while (!isdigit(c)) w|=c=='-',c=getchar();
while (isdigit(c)) x=x*10+(c^48),c=getchar();
if(w) x=-x;
}
template <typename T>
inline void write(T x) {
if(x<0) putchar('-'),x=-x;
if(x<10) putchar(x+'0');
else write(x/10),putchar(x%10+'0');
}
template <typename T>
inline void writeln(T x) {
write(x);
putchar('\n');
}
}
using IO::read;
using IO::write;
using IO::writeln;
const int N=2e5+5;
const LL inf=2e18;
struct Node {
int ch[2];
int sum;
};
class sgt {
public:
Node t[20000000];
int nodecnt;
void init() {
nodecnt=0;
}
int newnode() {
int p=++nodecnt;
t[p].ch[0]=t[p].ch[1]=t[p].sum=0;
return p;
}
void insert(int &p,LL l,LL r,LL pos) {
if(!p) p=newnode();
t[p].sum++;
if(l==r) return;
LL mid=(l+r)>>1;
if(pos<=mid) insert(t[p].ch[0],l,mid,pos);
else insert(t[p].ch[1],mid+1,r,pos);
}
int query(int p,LL l,LL r,LL ql,LL qr) {
if(!p) return 0;
if(ql<=l && r<=qr) return t[p].sum;
LL mid=(l+r)>>1;
int ret=0;
if(ql<=mid) ret=query(t[p].ch[0],l,mid,ql,qr);
if(qr>mid) ret+=query(t[p].ch[1],mid+1,r,ql,qr);
return ret;
}
int merge(int x,int y) {
if(!x || !y) return x|y;
t[x].sum+=t[y].sum;
t[x].ch[0]=merge(t[x].ch[0],t[y].ch[0]);
t[x].ch[1]=merge(t[x].ch[1],t[y].ch[1]);
return x;
}
} T;
vector<int> pt1;
vector<int> stapple[N],t[N];
vector<pair<int,LL> > query[N];
int n,m,L,C,bans,bane;
int a[N],b[N],Q,fa[N],d[N],vis[N],rt[N];
LL dep[N],ans[N];
void dfs(int np) {
vis[np]=1,pt1.push_back(np);
for(int &x:stapple[np]) T.insert(rt[np],0,inf,x+dep[np]);
for(int &x:t[np]) {
if(np==bans && x==bane) continue;
dep[x]=dep[np]+d[x];
dfs(x);
rt[np]=T.merge(rt[np],rt[x]);
}
for(auto &x:query[np])
ans[x.first]=T.query(rt[np],0,2e18,dep[np],dep[np]+x.second);
}
void solve(int p) {
T.init();
int cx=p;
pt1.clear();
while(!vis[cx]) vis[cx]=1,cx=fa[cx];
bans=fa[cx],bane=cx,dfs(cx);
LL totlen=dep[bans]+d[cx];
T.init();
vector<LL> cur;
for(int &x:pt1) for(int &y:stapple[x])
cur.push_back(y+dep[x]+d[cx]+dep[bans]);
sort(cur.begin(),cur.end());
vector<pair<LL,LL> > c2;
for(int i=bans; i; i=fa[i]) {
for(auto &t:query[i])
c2.push_back(make_pair(t.second+totlen+dep[i],t.first));
if(i==bane) break;
}
sort(c2.begin(),c2.end());
LL vl=0;
int R=0;
for(int i=0,j=0; i<(int)c2.size(); i++) {
while(j<(int)cur.size() && cur[j]<=c2[i].first) {
T.insert(R,0,totlen,cur[j]%totlen);
vl+=cur[j]/totlen;
j++;
}
ans[c2[i].second]-=vl;
ans[c2[i].second]+=1LL*(c2[i].first/totlen)*T.query(R,0,totlen,0,c2[i].first%totlen);
ans[c2[i].second]+=1LL*(c2[i].first/totlen-1)*T.query(R,0,totlen,c2[i].first%totlen+1,totlen);
}
}
int main() {
read(n);
read(m);
read(L);
read(C);
for(int i=1; i<=n; i++) {
read(a[i]);
a[i]=L-1-a[i];
}
for(int i=1; i<=m; i++) {
read(b[i]);
b[i]=L-1-b[i];
}
reverse(a+1,a+n+1);
reverse(b+1,b+m+1);
for(int i=1; i<=n; i++) {
int nxt=(a[i]+C)%L;
if(nxt>a[n])fa[i]=1,d[i]=L+a[1]-nxt+C;
else fa[i]=lower_bound(a+1,a+n+1,nxt)-a,d[i]=a[fa[i]]-nxt+C;
}
for(int i=1; i<=m; i++) {
if(b[i]>a[n]) stapple[1].push_back(a[1]+L-b[i]);
else {
int cur=lower_bound(a+1,a+n+1,b[i])-a;
stapple[cur].push_back(a[cur]-b[i]);
}
}
read(Q);
for(int i=1; i<=Q; i++) {
int p;
LL T;
read(p);
read(T);
query[n+1-p].push_back(make_pair(i,T));
}
for(int i=1; i<=n; i++) t[fa[i]].push_back(i);
for(int i=1; i<=n; i++)
if(!vis[i]) solve(i);
for(int i=1; i<=Q; i++) writeln(ans[i]);
return 0;
}
T3 Stray
原题
CSDN下载:https://download.csdn.net/download/Ljnoit/12262096
链接
翻译
题目描述
这是一道通信题。
Anthony 是一只生活在 JOI 市的蚂蚁。JOI 市共有被划分为 N N N 个町,编号为 0 0 0 到 N − 1 N−1 N−1。Anthony 居住在 0 0 0 号町。总共有 M M M 条路,编号为 0 0 0 到 M − 1 M−1 M−1。第 i i i 条路连接编号为 U i U_i Ui 和 V i V_i Vi 的町 ( U i ≠ V i U_i≠V_i Ui=Vi),每条路是双向的。保证不存在两条连接两个相同的町的道路,同时每个点可以直接或间接互相到达。
Catherine 是一只猫,也是 Anthony 的好朋友。她打算游览 JOI 市,但是她并不知道关于路的信息,所以经常迷路。于是,Anthony 打算事先在每条道路上都作上标记。标记共有 A A A 种,编号为 0 0 0 到 A − 1 A−1 A−1。
Catherine 现在到达了 JOI 市。当她不在 0 0 0 号町时,她会做如下事情:
- 对于每种标记,她会统计当前所在的町连出去的道路中,除了她上一次走来的道路(若存在)之外,这种标记共出现了多少次。
之后她会选择一条路去行动。注意除了上一次走来路,她仅能通过路上的标记来分辨其余的道路。她想尽量快地到达 0 0 0 号町。更准确地说,若起点至 0 0 0 号町最少需要经过 d d d 条道路,那么她希望在经过不超过 d + B d+B d+B 条边后就能到达 0 0 0 号町。
你需要编写程序模拟 Anthony 摆放路标和 Catherine 探索 JOI 城的过程。
交互细节
你需要提交两个程序。
第一个程序为Anthony.cpp
,它会模拟Anthony的摆放过程。需要包含头文件Anthony.h
。
-
std::vector<int> Mark(int N, int M, int A, int B, std::vector<int> U, std::vector<int> V)
该程序会恰好在开始时调用一次。- 数字 N , M , A , B N,M,A,B N,M,A,B 含义同题面。
- U , V U,V U,V存储着所有的边, U [ i ] 和 V [ i ] U[i] 和 V[i] U[i]和V[i] 表示第i条路连接的街道 U i 和 V i U_i 和 V_i Ui和Vi。
选手需要返回一个长度为 M M M 的数组 x x x,第 i i i 个元素 x i x_i xi 代表在第 i i i 条道路上的路标编号。
若 x x x 长度不为 M M M,测评结果为 “Wrong Answer [1]”。若不满足 0 ≤ x i ≤ A − 1 0≤xi≤A−1 0≤xi≤A−1,测评结果为 “Wrong Answer [2]”。
第二个程序为Catherine.cpp
,它会模拟Catherine的探索过程。需要包含头文件Catherine.h
。
-
void Init(int A, int B)
该程序会恰好在开始时调用一次。- 数字 A , B A,B A,B 含义同题面。
-
int Move(std::vector<int> y)
该程序会在每次Catherine到达一个不是0的街道时调用。- y 是一个长度为A的数组,元素 yj 代表着所有与当前街道相连且不为她上一次走过的路(若存在)的路上标记 j 的出现次数。
- 你需要返回一个整数 z z z,表示选择走的标记。需要满足 − 1 ≤ z ≤ A − 1 −1≤z≤A−1 −1≤z≤A−1。若不满足 − 1 ≤ z ≤ A − 1 −1≤z≤A−1 −1≤z≤A−1,测评结果为 “Wrong Answer [3]”。若 z = − 1 z=−1 z=−1,表示Catherine原路返回,此时若Catherine未进行过任何操作,测评结果为 “Wrong Answer [4]”。若 0 ≤ z ≤ A − 1 0≤z≤A−1 0≤z≤A−1,表示 Catherine 选择一条标记为 z 的边经过,此时若 y z = 0 yz=0 yz=0,测评结果为 “Wrong Answer [5]”。
注意如果有多条可以选择的边,
grader
不一定会随机选择一条合法的出现行动。如果 Catherine 未能在 D + b D+b D+b 步内到达街道0,测评结果为 “Wrong Answer [6]”。
注意事项
选手程序可能会运用若干全局变量和子程序。为了防止多个文件变量重名或子程序重名带来的编译错误,请将所有全局变量和子程序定义在一个没有名字的 namespace 里。
如果在程序里使用 printf/scanf/cout/cin
,会直接导致Wrong Answer 或者 Runtime Error。
最终测评时会将 Anthony 和 Catherine 的程序独立编译,因此不能共享全局变量。
编译与运行
将grader.cpp
, Anthony.cpp
,Catherine.cpp
,Anthony.h
,Catherine.h
放在同一文件夹下,在终端内运行如下命令
g++ -O2 -o grader grader.cpp Anthony.cpp Catherine.cpp
编译成功时会得到一个可执行文件grader
。
可执行文件从标准输入种读入以下内容:
第一行五个数字
N
,
M
,
A
,
B
,
S
N,M,A,B,S
N,M,A,B,S。其中
S
S
S表示起点。
接下来
M
M
M行,每行两个数字
U
i
,
V
i
U_i,V_i
Ui,Vi,表示一条道路。
可执行文件回想标准输出流输出以下内容。
若程序运行时产生错误 [1] 至 [5],则会输出形如 “Wrong Answer [1]” 的错误信息。
否则如果未在 N+B 步内到达街道1,输出 “Wrong Answer; Number of moves > N + B”.
否则按照 “Number of moves = 4” 的格式输出信息。
数据范围
- 子任务1 (2分): A = 4 , B = 0 , M = N − 1 A=4,B=0,M=N−1 A=4,B=0,M=N−1。
- 子任务2 (2分): A = 4 , B = 0 A=4,B=0 A=4,B=0。
- 子任务3 (2分): A = 3 , B = 0 , M = N − 1 A=3,B=0,M=N−1 A=3,B=0,M=N−1。
- 子任务4 (9分): A = 3 , B = 0 A=3,B=0 A=3,B=0。
- 子任务5 (5分): A = 2 , B = 2 N , M = N − 1 , 6 ≤ N ≤ 500 A=2,B=2N,M=N−1,6≤N≤500 A=2,B=2N,M=N−1,6≤N≤500。
- 子任务6 (71分): A = 2 , B = 12 , M = N − 1 A=2,B=12,M=N−1 A=2,B=12,M=N−1。
- 子任务7 (9分): A = 2 , B = 6 , M = N − 1 A=2,B=6,M=N−1 A=2,B=6,M=N−1。
对于所有测试数据,满足 2 ≤ N ≤ 20000 , 1 ≤ M ≤ 20000 , 1 ≤ S < N 2≤N≤20000,1≤M≤20000,1≤S<N 2≤N≤20000,1≤M≤20000,1≤S<N。
保证图联通且无重边无自环。
时间限制:2s
空间限制:1GB
解析
对于第一类子任务。我们只能走最短路。
考虑求出这张图的 BFS 树,显然,所有边要么是连接相邻两层的点(异层边),要么是连接同层的点(同层边)。
如果没有同层边,那么我们只要对于每一层的边做同一种标记,按 0,1,2 循环标记即可。因为这样的话,在某一个点上,即使有两种存在的标记,我们也可以确定哪一个是往上走的。
那么如果有了同层边,其实也很简单,只要做它下面的那一层的边的标记即可。
这样我们依旧可以确定哪一条边是向上的。
对于第二类子任务,只有两种标记,但图是一个树,而且允许多走六步。
我们先考虑把树划分为若干条链,如果一个点有至少两个儿子,那么称之为分叉点。我们希望标记能够满足:
对于每个分叉点,到父亲的边和到儿子的边的标记不同。
每一条链上都是 110010 的循环位移。
那么对于代码 B,它先走三步,如果中途走到非链的部分就直接确定了方向。否则,它还在链上,而且可以确定周围长度为 5 的子串的样子,这样的话,一定可以确定它是在往上走还是往下走,这样也确定了方向,而且最多多走 6 步。
代码
Anthony.cpp:
#include "Anthony.h"
#include <bits/stdc++.h>
using namespace std;
vector<int> Mark(int n,int m,int A,int B,vector<int> U,vector<int> V) {
vector<int> dist(n, -1);
vector<vector<int> > G(n);
for (int i=0; i<m; i++) {
G[U[i]].push_back(V[i]);
G[V[i]].push_back(U[i]);
}
dist[0]=0;
queue<int> que;
que.push(0);
while(!que.empty()) {
int u=que.front();
que.pop();
for(int v:G[u]) {
if(dist[v]==-1) {
dist[v]=dist[u] + 1;
que.push(v);
}
}
}
vector<int> ret(m);
if(A>=3) {
for(int i=0; i<m; i++) {
ret[i]=min(dist[U[i]],dist[V[i]])%3;
}
} else {
const int arr[6]={1,1,0,0,1,0};
for (int i=0; i<n; i++) G[i].clear();
for (int i=0; i<m; i++) {
if(dist[U[i]]>dist[V[i]]) swap(U[i], V[i]);
G[U[i]].push_back(i);
}
function<void(int, int)> dfs=[&](int u,int fr) {
if((int)G[u].size() == 1) {
int p;
if(fr==-1 || ret[fr]==0) {
p = 0;
} else {
p = 2;
}
while((int)G[u].size()==1) {
int i=G[u][0];
int v=V[i];
u=v;
fr=i;
ret[fr]=arr[p];
p=(p+1)%6;
}
}
int c=(fr==-1 ? 1 : ret[fr]^1);
for (int i:G[u]) {
int v=V[i];
ret[i]=c;
dfs(v,i);
}
};
dfs(0,-1);
}
return ret;
}
Catherine.cpp:
#include "Catherine.h"
#include <bits/stdc++.h>
using namespace std;
namespace {
const int arr[6]={1,1,0,0,1,0};
int A,on,fir,last;
vector<int> vec;
vector<int> cs;
inline bool bad(vector<int> v) {
vector<int> al;
for(int i=0; i<6; i++) al.push_back(arr[i]);
for(int i=0; i<6; i++) al.push_back(arr[i]);
for(int i=0; i<8; i++) {
int flag=1;
for(int j=0; j<5; j++) if(al[i+j]!=v[j]) {
flag=0;
break;
}
if(flag) return true;
}
return false;
}
}
void Init(int _A, int B) {
A=_A;
on=0;
fir=1;
last=-1;
}
int Move(vector<int> y) {
if(A>=3) {
int cc=0;
for(int i=0; i<3; i++) cc+=(y[i]!=0 ? 1 : 0);
if(cc==1) {
if(y[0]) return 0;
else if(y[1]) return 1;
else return 2;
} else {
if(y[0] && y[1]) return 0;
else if(y[1] && y[2]) return 1;
else return 2;
}
} else {
if(on) {
if(y[0]==1 && y[1]!=1) return last=0;
else if(y[0]!=1 && y[1]==1) return last=1;
else return last^=1;
}
if(fir) {
fir=0;
if(y[0]!=1 && y[1]!=1) {
if(y[0]) last = 0;
else last = 1;
y[last]--;
cs=y;
vec.push_back(last);
return last;
}
if(y[0]==1) last=0;
else last = 1;
if(y[last^1]!=1) {
on=1;
return last;
}
y[last]--;
cs=y;
vec.push_back(last);
return last;
}
if(y[0]!=1 && y[1]!=1) {
on=1;
return -1;
}
if(y[0]==1 && y[1]==1) {
on=1;
return last^=1;
}
if(y[0]>1) {
on=1;
return last=1;
}
if(y[1] >1) {
on=1;
return last=0;
}
if(vec.size()<3) {
if(y[0]) last=0;
else last=1;
vec.push_back(last);
return last;
}
vector<int> v;
v.push_back(cs[0] ? 0 : 1);
for(int i=0; i<3; i++) {
v.push_back(vec[i]);
}
v.push_back(y[0] ? 0 : 1);
on=1;
if(bad(v)) {
return -1;
} else {
return last=v.back();
}
}
}