ICPC 2019-2020 North-Western Russia Regional Contest

链接

题目链接

题目

A. Accurate Movement

模拟即可。

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
#include <bits/stdc++.h>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline int read()
{
int x=0; char ch=getchar(); bool f=0;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return f?-x:x;
}
#define CASET fo(___,1,read())
int a,b,n,ans;
int main()
{
a=read(); b=read(); n=read();
int d=b-a;
for(;b<n;)
{
if(a<n) a=b,ans++;
b+=d,ans++;
}
if(a<n) a=b,ans++;
printf("%d\n",ans);
return 0;
}

B. Bad Treap

题意

找出 $n$ 个在 int 范围内的整数,满足

$a_i<a_{i+1}$ 且 $\sin(a_i)<\sin(a_{i+1})$。

$n\le 50000$

题解

设 $a_i=k(i+b)$,那也就是要找一个整数 $k$,使得 $k>2\omega \pi$ 且 $k-2\omega \pi$ 的值尽量小。

打表发现,$k=710$ 效果很好。

C. Cross-Stitch

答案下界显然是 $4n-1$,其中 $n$ 为 X 的个数。

可以发现,每个 X 中距离为 $1$ 的情况只需要用到竖着的两条。因为只用竖着的两条,最终可以回到本身这个节点,也可以去到原节点同一列的节点上。

建出图后,跑个有限制的欧拉回路就可以了。

比赛的时候忘在dfs里加限制了。。。

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
#include <bits/stdc++.h>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline int read()
{
int x=0; char ch=getchar(); bool f=0;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return f?-x:x;
}
#define CASET fo(___,1,read())

const int N=600000;
int n,m,st[N],top;
int ver[N],ne[N],head[N],tot=1,n1,n2;
bool vis[N];
inline void add(int x,int y)
{
ver[++tot]=y; ne[tot]=head[x]; head[x]=tot;
ver[++tot]=x; ne[tot]=head[y]; head[y]=tot;
}
inline int id(int x,int y,int z)
{
return x+y*n1+z*n2;
}

void dfs(int u,int las)
{
for(int &i=head[u];i;i=ne[i])
if(!vis[i]&&(las==-1||((u/n2)!=(ver[i]/n2)||(las!=ver[i]/n2))))
{
vis[i]=vis[i^1]=1;
dfs(ver[i],(u/n2)==(ver[i]/n2)?ver[i]/n2:-1);
}
st[++top]=u;
}

inline void pr(int x)
{
x%=n2;
printf("%d %d\n",x/n1,x%n1);
}

inline void print(int k)
{
if(k==1) return;
print(k-1);
if(abs(st[k]-st[k-1])!=n2)
pr(st[k]);
}
char s[105];
int sum;
int main()
{
n=read(); m=read();
swap(n,m);
n1=(max(n,m)+5); n2=(max(n,m)+5)*(max(n,m)+5);
int xx=-1,yy=-1;
fo(i,1,n)
{
scanf("%s",s+1);
fo(j,1,m)
if(s[j]=='X')
{
fo(a,0,1)
fo(b,0,1)
add(id(i-a,j-b,0),id(i-a,j-b,1));
add(id(i-1,j,1),id(i,j,1));
add(id(i-1,j-1,1),id(i,j-1,1));
add(id(i-1,j-1,0),id(i,j,0));
add(id(i-1,j,0),id(i,j-1,0));
xx=i; yy=j;
sum++;
}
}
if(xx==-1)
{
printf("-1\n");
return 0;
}
printf("%d\n",sum*4-1);
dfs(id(xx,yy,0),-1);
print(top);
return 0;
}

D. Double Palindrome

看来长时间没看,Border和回文理论有些忘了,在这写下证明吧QAQ。

一些证明

引理1(Weak Periodicity Lemma):对一字符串 $s$,若 $p,q$ 为 $s$ 的周期,且 $p+q\le |s|$,则 $\gcd(p,q)$ 为 $s$ 的周期。

证明:

不妨设 $p\geq q$。设 $d=p-q$。

由 $p,q$ 为 $s$ 的周期,可知:$s_i=s_{i+p}(i\le |s|-p),s_i=s_{i+q}(i\le |s|-q)$。

当 $i> |q|$ 时,$s_i=s_{i-q}=s_{i-q+p}=s_{i+d}$。

当 $i\le |s|-p$ 时,$s_i=s_{i+p}=s_{i+p-q}=s_{i+d}$。

因此 $d=p-q$ 为 $s$ 的一个周期。

由辗转相除法,$\gcd(p,q)$ 为 $s$ 的周期。

证毕。

注意:真正的周期引理的限制条件可改为 $p+q-\gcd(p,q)\le |s|$。

引理2:若一字符串 $s$ 可以表示成至少两种不同的非空双回文划分,则字符串 $s$ 为整周期串。

证明:

记 $s[l,r]$ 表示字符串 $s$ 从第 $l$ 到第 $r$ 位形成的子串。

设 $s$ 的两种非空双回文划分为:$s[1,x]+s[x+1,|s|],s[1,y]+s[y+1,|s|]$,其中 $1\le x < y < |s|$,且 $s[1,x],s[x+1,s],s[1,y],s[y+1,|s|]$ 均为回文串。

可以发现,$y-x$ 和 $|s|-(y-x)$ 均为 $s$ 的周期,

由引理一,$\gcd(y-x,|s|-(y-x))=\gcd(y-x,|s|)$ 也为 $s$ 的周期。

而 $\gcd(y-x,|s|)|s$,证毕。

题意

用前 $k$ 个小写字符,组成长度至多为 $n$ 的字符串。

问有多少个串是弱双回文划分。

$n\le 10^5$,

题解

首先,设一个 $g(n)$ 表示暴力划分的双回文的种类数。

$g(n)=\sum_{i=1}^nk^{\lfloor \frac{i+1}{2}\rfloor}k^{\lfloor \frac{n-i+1}{2}\rfloor}$。

这样统计显然会算重,而多的自然是能被两种及以上划分的字符串。

由引理二,这样的字符串为整周期串。

于是,我们统计字符串的时候只统计只有一种划分的字符串,设长度为 $i$ 的合法字符串个数为 $f(i)$,那么答案就是 $\sum_{i=1}^nf(i)\lfloor \frac{n}{i} \rfloor$。

考虑求出 $f(n)$,用 $g(n)$ 减去不合法的方案,枚举整周期串的最小正周期 $i$,那么复制 $\frac{n}{i}$ 倍后形成的串就在 $g(n)$ 中算了 $\frac{n}{i}$ 次,减去即可。$f(n)=g(n)-\sum_{i\neq n,i|n} f(i)\frac{n}{i}$。

时间复杂度 $O(n\log 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
46
47
48
49
50
51
#include <bits/stdc++.h>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline int read()
{
int x=0; char ch=getchar(); bool f=0;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return f?-x:x;
}
#define CASET fo(___,1,read())
const ll mod=998244353;
inline ll Add(ll x,ll y){x+=y; return (x<mod)?x:x-mod;}
inline ll Dec(ll x,ll y){x-=y; return (x<0)?x+mod:x;}
inline ll Mul(ll x,ll y){return x*y%mod;}
inline ll Pow(ll x,ll y)
{
y%=(mod-1);ll ans=1;for(;y;y>>=1,x=x*x%mod)if(y&1) ans=ans*x%mod;
return ans;
}
int n,k;
ll f[100010],g[100010];
int main()
{
n=read(); k=read();
fo(i,1,n)
f[i]=g[i]=Add(Mul(i/2,Pow(k,(i+1)/2)),Mul((i+1)/2,Pow(k,(i+2)/2)));
fo(i,1,n)
fo(j,2,n/i)
f[i*j]=Dec(f[i*j],f[i]*j%mod);
ll ans=0;
fo(i,1,n)
ans=Add(ans,f[i]*(n/i)%mod);
printf("%lld\n",ans);
return 0;
}

E. Equidistant

题意

一棵树,给定 $m$ 个点,问是否存在一个点,使得该点到另外 $m$ 个点的距离相同。

$n,m\le 2\times 10^5$。

题解

$m$ 个点一起bfs即可。

代码

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
#include <bits/stdc++.h>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline int read()
{
int x=0; char ch=getchar(); bool f=0;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return f?-x:x;
}
#define CASET fo(___,1,read())
const int N=200010;
vector<int> adj[N];
queue<int> q;
int n,m;
inline void add(int x,int y)
{
adj[x].pb(y); adj[y].pb(x);
}
int t[N],vis[N],d[N];
void bfs()
{
for(;!q.empty();)
{
int u=q.front(); q.pop();
if(t[u]==m)
{
printf("YES\n%d",u);
return;
}
for(auto v:adj[u])
{
if(!d[v]||d[v]==d[u]+1)
{
d[v]=d[u]+1;
t[v]+=t[u];
if(!vis[v])
{
vis[v]=1; q.push(v);
}
}
}
}
printf("NO");
}

int main()
{
n=read(); m=read();
fo(i,2,n) add(read(),read());
int x;
fo(i,1,m) x=read(),q.push(x),vis[x]=1,d[x]=1,t[x]=1;
bfs();
return 0;
}

H. High Load Database

题意

一个数组,将数组分为若干段连续的子段,使得每个子段之和不超过 $k$。

$m$ 次询问,每次询问给定 $k$,问最少的段数。

$n\le 2\times 10^5,q\le 10^5,\sum a_i\le 10^6,a_i\geq 1$。

题解

$\sum a_i\le 10^6$,可以发现对于每个 $k$,如果暴力用二分求出答案,时间复杂度大概是 $O(\frac{n}{k}\log n)$。

因此,通过记忆化的方法,$q$ 次询问总的最坏的复杂度就是 $O(n\log q\log n)$。

J. Just the Last Digit

从小到大枚举 $i$,枚举 $j$,然后判断 $i,j$ 是否有边相连。

判断的方法是算出除 $(i,j)$ 边外 $i$ 到 $j$ 路径条数模 $10$ 后的结果即可。

时间复杂度 $O(n^3)$。

L. Lengths and Periods

题意

对一字符串 $|s|$,设 $\alpha$ 为最大的 $\frac{|s|}{|t|}$,使得 $t$ 为 $s$ 的周期。

给定一字符串,问该字符串的子串最大的 $\alpha$ 是多少。

$|s|\le 2\times 10^5$。

题解

题目所求转换为:

选择任意两个后缀 $i,j(i<j)$,求出 $lcp(i,j)$,求 $\frac{lcp(i,j)+(j-i)}{j-i}$ 的最大值。

于是可以建出 SA,求出height数组,然后启发式合并即可。

时间复杂度 $O(n\log ^2n)$。