ICPC 2019-2020 North-Western Russia Regional Contest
1|0Preface
经过休整后终于等到一次三人合训,然后经典徐神带飞
感觉最近要过年了大家有空的时间都变少了,后面一起训练的机会可能就不多了,正好滚去刷一下之前欠下的CF
2|0A. Accurate Movement
签到,第一步移动完后后面就每次花费两步把两个一起向右移动b−a即可
#include<cstdio>
#include<iostream>
using namespace std;
int a,b,n;
int main()
{
scanf("%d%d%d",&a,&b,&n); int k=b-a;
return printf("%d",(n-b+k-1)/k*2+1),0;
}
3|0B. Bad Treap
首先这题很容易想到通过构造一个序列{xi},使得其本身以及sinxi都单调
手玩一下会发现如果我们找到一个x0,满足sinx0>0且sinx0的值尽可能小,那么我们只需要形如x0,2x0,3x0,⋯构造即可
写个程序找出的数是710,但直接这么构造精度还不够,把负数的半边也用上后即可通过
from math import *
n = int(input())
# ans = 1e18
# anst = 0
# for i in range(1, 100001):
# if sin(i) > 0 and sin(i) < ans:
# ans = sin(i)
# anst = i
for i in map(lambda x: (x - 25000) * 710, range(1, n + 1)):
print(i)
4|0C. Cross-Stitch
观察题,发现有解的充要条件后就很简单了
考虑对于每个X连四条边,两条正面的连接对角线,两条反面的连接左上、左下,右上、右下
不难发现建出的图每个点的度数一定是偶数,并且由于保证了连通性,因此这个图一定存在欧拉回路
而分析一下会发现这个图的欧拉回路同时满足了经过正面的所有边一次,以及行走的总距离最短
因此写一个支持交替走两种边的欧拉回路,最后删除其中最后一条背面的边即可
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pi;
const int N=105,M=1e6+5;
struct edge
{
int to,nxt,id;
}e[2][M]; int n,m,head[2][N*N],cnt[2],id[N][N],idx,vis[M];
pi rid[N*N]; char s[N][N]; vector <pi> path;
inline void addedge(CI x,CI y,CI z,CI tp)
{
e[tp][++cnt[tp]]=(edge){y,head[tp][x],z}; head[tp][x]=cnt[tp];
e[tp][++cnt[tp]]=(edge){x,head[tp][y],z}; head[tp][y]=cnt[tp];
}
inline void DFS(CI now,CI tp)
{
for (RI& i=head[tp][now];i;i=e[tp][i].nxt)
{
int to=e[tp][i].to,eid=e[tp][i].id;
if (vis[eid]) continue; vis[eid]=1;
DFS(to,tp^1); path.push_back(pi(now,to));
}
}
int main()
{
RI i,j; for (scanf("%d%d",&m,&n),i=1;i<=n;++i) scanf("%s",s[i]+1);
for (i=0;i<=n;++i) for (j=0;j<=m;++j) ++idx,id[i][j]=idx,rid[idx]=pi(i,j);
for (idx=0,i=1;i<=n;++i) for (j=1;j<=m;++j) if (s[i][j]=='X')
{
addedge(id[i-1][j-1],id[i][j],++idx,0);
addedge(id[i-1][j],id[i][j-1],++idx,0);
addedge(id[i-1][j-1],id[i][j-1],++idx,1);
addedge(id[i-1][j],id[i][j],++idx,1);
}
for (i=1;i<=n;++i) for (j=1;j<=m;++j) if (s[i][j]=='X')
{
DFS(id[i][j],0); reverse(path.begin(),path.end());
printf("%d\n",path.size()-1);
printf("%d %d\n",rid[path[0].fi].se,rid[path[0].fi].fi);
printf("%d %d\n",rid[path[0].se].se,rid[path[0].se].fi);
for (i=1;i<path.size()-1;++i)
printf("%d %d\n",rid[path[i].se].se,rid[path[i].se].fi);
return 0;
}
return 0;
}
5|0D. Double Palindrome
比赛的时候队友已经给出了去重的关键结论了,但可惜最后统计的时候状态没设计好变得很繁琐导致没能做出这题
首先考虑设fx表示不考虑重复时长度为x的双回文串的方案数,我们暴力枚举其中一个回文串的长度得到式子:
现在的问题就是当存在某个串S可以拆分成多个回文串时上述的方法会算重,但手玩一下会发现若某个串的划分方式不唯一,则它一定可以写作m个s的形式,其中s是一个划分方式唯一的串
根据上面fx的计算式会发现这个串恰好被计算了m次,因此可以容斥得到划分方案唯一的双回文串个数g(x)
最后的答案就是∑ni=1⌊ni⌋×gi,总复杂度O(nlogn)
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005,mod=998244353;
int n,k,f[N];
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
int main()
{
RI i,j; for (scanf("%d%d",&n,&k),i=1;i<=n;++i)
if (i%2==1) f[i]=1LL*i*quick_pow(k,(i+1)/2)%mod;
else f[i]=(1LL*(i/2)*quick_pow(k,i/2)%mod+1LL*(i/2)*quick_pow(k,i/2+1)%mod)%mod;
for (i=1;i<=n;++i) for (j=i*2;j<=n;j+=i)
f[j]=(f[j]-1LL*(j/i)*f[i]%mod+mod)%mod;
int ans=0; for (i=1;i<=n;++i) (ans+=1LL*(n/i)*f[i]%mod)%=mod;
return printf("%d",ans),0;
}
6|0E. Equidistant
不难发现如果我们求出每个点到所有关键点的距离的最大值和最小值,则这两个值相等就是题设的充要条件
大力换根DP维护这两个值即可
#include<cstdio>
#include<iostream>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005,INF=1e9;
int n,m,x,y,key[N],mn[N],smn[N],mx[N],smx[N]; vector <int> v[N];
inline void Upt_min(CI x,CI y)
{
if (y<mn[x]) smn[x]=mn[x],mn[x]=y;
else if (y<smn[x]) smn[x]=y;
}
inline void Upt_max(CI x,CI y)
{
if (y>mx[x]) smx[x]=mx[x],mx[x]=y;
else if (y>smx[x]) smx[x]=y;
}
inline void DFS1(CI now=1,CI fa=0)
{
mn[now]=smn[now]=INF; mx[now]=smx[now]=-INF;
if (key[now]) mn[now]=mx[now]=0;
for (auto to:v[now]) if (to!=fa)
DFS1(to,now),Upt_min(now,mn[to]+1),Upt_max(now,mx[to]+1);
}
inline void DFS2(CI now=1,CI fa=0)
{
for (auto to:v[now]) if (to!=fa)
{
if (mn[now]==mn[to]+1) Upt_min(to,smn[now]+1); else Upt_min(to,mn[now]+1);
if (mx[now]==mx[to]+1) Upt_max(to,smx[now]+1); else Upt_max(to,mx[now]+1);
DFS2(to,now);
}
}
int main()
{
RI i; for (scanf("%d%d",&n,&m),i=1;i<n;++i)
scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
for (i=1;i<=m;++i) scanf("%d",&x),key[x]=1;
//for (DFS1(),DFS2(),i=1;i<=n;++i) printf("MN=%d MX=%d\n",mn[i],mx[i]);
for (DFS1(),DFS2(),i=1;i<=n;++i)
if (mn[i]==mx[i]) return printf("YES\n%d",i),0;
return puts("NO"),0;
}
7|0F. Foreach
防AK题,策不来一点
8|0G. Golf Time
防AK题连坐
9|0H. High Load Database
注意到∑ai≤106,因此很容易想到根号分治,设阈值S
- 当ti≤S时,本质不同的询问只有S种,可以直接O(n)跑一遍贪心求解
- 当ti>S时,每次二分找位置,需要跳的次数为∑aiS
tradeoff一下得到当S=√∑ai×logn时,最优复杂度为O(q×S)
#include<cstdio>
#include<iostream>
#include<cctype>
#include<algorithm>
#define RI register int
#define CI const int&
#define Tp template <typename T>
using namespace std;
const int N=200005,S=2000;
int n,a[N],pfx[N],q,t[N],mx,ans[S+5],vis[S+5];
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
char Fin[S],*A,*B;
public:
Tp inline void read(T& x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
#undef tc
}F;
int main()
{
//freopen("H.in","r",stdin); freopen("H.out","w",stdout);
RI i,j; for (F.read(n),i=1;i<=n;++i)
F.read(a[i]),pfx[i]=pfx[i-1]+a[i],mx=max(mx,a[i]);
for (F.read(q),i=1;i<=q;++i) if (F.read(t[i]),t[i]<=S) vis[t[i]]=1;
for (i=1;i<=S;++i) if (vis[i])
{
if (mx>i) { ans[i]=-1; continue; }
for (j=1;j<=n;)
{
int k=j; while (k<=n&&pfx[k]-pfx[j-1]<=i) ++k;
++ans[i]; j=k;
}
}
for (i=1;i<=q;++i)
{
if (t[i]<=S)
{
if (ans[t[i]]==-1) puts("Impossible"); else printf("%d\n",ans[t[i]]);
continue;
}
if (mx>t[i]) { puts("Impossible"); continue; }
int ret=0; for (j=1;j<=n;)
++ret,j=upper_bound(pfx+1,pfx+n+1,pfx[j-1]+t[i])-pfx;
printf("%d\n",ret);
}
return 0;
}
10|0I. Ideal Pyramid
手玩一下会发现对于某个位置,其在金字塔上的高度就是它到四条边距离的最小值
那么很容易想到二分金字塔高度,这样合法的金字塔中心就在一个正方形区域内了,只要判断若干个正方形是否有交即可
但后面祁神发现有更简单的做法,不需要二分我们也能作出若干个正方形,而最后要求一个大的正方形包住所有的正方形
直接维护四个方向上的最值即可,注意最后的大正方形的边长必须是偶数,复杂度O(n)(这题复杂度诈骗,搞得都不敢写)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e3+5;
const int INF = 1e9+5;
int n;
signed main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n;
int x1=INF, x2=-INF, y1=INF, y2=-INF;
for (int i=1; i<=n; ++i){
int x, y, h; cin >> x >> y >> h;
x1 = min(x1, x-h), x2 = max(x2, x+h);
y1 = min(y1, y-h), y2 = max(y2, y+h);
}
int H = max((x2-x1+1)/2, (y2-y1+1)/2);
cout << x1+H << ' ' << y1+H << ' ' << H << '\n';
return 0;
}
11|0J. Just the Last Digit
想到突破口后就很简单的构造题
不难发现i→i+1的路径条数就反应了两点间是否有边,因此可以先推出所有相距为1的点对连边关系
然后考虑诸如i→i+2的路径条数,由于i→i+1,i+1→i+2已经知道,可以算出路径数量然后根据误差判断是否要加上i→i+2的边
依此类推即可,总复杂度O(n3)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 505;
int n;
char A[N][N];
int ans[N][N];
int dis[N][N];
signed main(){
// ios::sync_with_stdio(0); cin.tie(0);
// cin >> n;
scanf("%lld", &n);
for (int i=1; i<=n; ++i){
scanf("%s", A[i]+1);
}
for (int k=1; k<=n; ++k){
for (int i=1; i+k<=n; ++i){
int v = i+k;
for (int w=1; w<k; ++w){
int x=i+w;
if (ans[x][v]) dis[i][v]=(dis[i][v]+dis[i][x])%10;
}
if (A[i][v]-'0' == dis[i][v]) ans[i][v]=0;
else if ((A[i][v]-'0' == (dis[i][v]+1)%10)) ans[i][v]=1, dis[i][v]=(dis[i][v]+1)%10;
}
}
for (int i=1; i<=n; ++i){
for (int j=1; j<=n; ++j){
printf("%lld", ans[i][j]);
}
puts("");
}
return 0;
}
12|0K. King's Children
我题意都没看,徐神开场开的一个构造题,细节爆多被关了3h,好在后面是写出来了
#include <bits/stdc++.h>
int main() {
std::ios::sync_with_stdio(false);
int n, m; std::cin >> n >> m;
std::vector<std::string> mat(n);
for(auto &s: mat) std::cin >> s;
std::map<char, int> cx, cy;
cx['('] = -1; cx[')'] = n; cy['('] = -1; cy[')'] = m;
for(int i = 0; i < n; ++i) for(int j = 0; j < m; ++j)
if(std::isupper(mat[i][j]))
cx[mat[i][j]] = i,
cy[mat[i][j]] = j;
int u = cx['A'] - 1, d = cx['A'] + 1, l = cy['A'] - 1, r = cy['A'] + 1, ma = 1;
for(auto &&[_1, U]: cx) for(auto &&[_2, D]: cx) {
for(auto &&[_3, L]: cy) for(auto &&[_4, R]: cy) {
if(D > cx['A'] && cx['A'] > U && R > cy['A'] && cy['A'] > L) {
// std::cerr << "Debug: " << U << ' ' << D << ' ' << L << ' ' << R;
bool flag = true;
for(auto &&[C, Cx]: cx) if(std::isupper(C) && C != 'A') {
auto Cy = cy[C];
if(D > Cx && Cx > U && R > Cy && Cy > L) {
flag = false;
break;
}
}
// std::cerr << ' ' << (flag ? "true" : "false") << std::endl;
if(flag && (D - U - 1) * (R - L - 1) > ma) {
ma = (D - U - 1) * (R - L - 1);
u = U, d = D, l = L, r = R;
}
}
}
}
for(int i = u + 1; i < d; ++i) for(int j = l + 1; j < r; ++j) mat[i][j] = 'A';
{
int uuu = -1;
for(int i = 0, j; i <= u; ++i) {
for(j = 0; j < m; ++j) if(std::isupper(mat[i][j])) break;
// std::cerr << "j = " << j << char(10);
if(j == m) {
if(uuu >= 0) for(int j = 0; j < m; ++j)
mat[i][j] = tolower(mat[i - 1][j]);
continue;
}
if(uuu < 0) uuu = i;
char t = mat[i][j];
for(int k = 0; k < j; ++k) mat[i][k] = t + 32;
for(; j < m; ++j) {
if(std::isupper(mat[i][j])) t = mat[i][j];
else mat[i][j] = t + 32;
}
}
for(int i = 0; i < uuu; ++i) for(int j = 0; j < m; ++j)
mat[i][j] = std::tolower(mat[uuu][j]);
}
{
int uuu = -1;
for(int i = n - 1, j; i >= d; --i) {
for(j = 0; j < m; ++j) if(std::isupper(mat[i][j])) break;
if(j == m) {
if(uuu >= 0) for(int j = 0; j < m; ++j)
mat[i][j] = tolower(mat[i + 1][j]);
continue;
}
if(uuu < 0) uuu = i;
char t = mat[i][j];
for(int k = 0; k < j; ++k) mat[i][k] = t + 32;
for(; j < m; ++j) {
if(std::isupper(mat[i][j])) t = mat[i][j];
else mat[i][j] = t + 32;
}
}
if(uuu >= 0) for(int i = n - 1; i > uuu; --i) for(int j = 0; j < m; ++j)
mat[i][j] = std::tolower(mat[uuu][j]);
}
{
int uuu = -1;
for(int i = 0, j; i <= l; ++i) {
for(j = u + 1; j < d; ++j) if(std::isupper(mat[j][i])) break;
if(j == d) {
if(uuu >= 0) for(int j = u + 1; j < d; ++j)
mat[j][i] = tolower(mat[j][i - 1]);
continue;
}
if(uuu < 0) uuu = i;
char t = mat[j][i];
for(int k = u + 1; k < j; ++k) mat[k][i] = t + 32;
for(; j < d; ++j) {
if(std::isupper(mat[j][i])) t = mat[j][i];
else mat[j][i] = t + 32;
}
}
if(uuu >= 0) for(int i = 0; i < uuu; ++i) for(int j = u + 1; j < d; ++j)
mat[j][i] = std::tolower(mat[j][uuu]);
}
{
int uuu = -1;
for(int i = m - 1, j; i >= r; --i) {
for(j = u + 1; j < d; ++j) if(std::isupper(mat[j][i])) break;
if(j == d) {
if(uuu >= 0) for(int j = u + 1; j < d; ++j)
mat[j][i] = tolower(mat[j][i + 1]);
continue;
}
if(uuu < 0) uuu = i;
char t = mat[j][i];
for(int k = u + 1; k < j; ++k) mat[k][i] = t + 32;
for(; j < d; ++j) {
if(std::isupper(mat[j][i])) t = mat[j][i];
else mat[j][i] = t + 32;
}
}
if(uuu >= 0) for(int i = m - 1; i > uuu; --i) for(int j = u + 1; j < d; ++j)
mat[j][i] = std::tolower(mat[j][uuu]);
}
for(int i = u + 1; i < d; ++i) for(int j = l + 1; j < r; ++j) if(i != cx['A'] || j != cy['A']) mat[i][j] = 'a';
for(int i = 0; i < n; ++i) std::cout << mat[i] << char(10);
return 0;
}
13|0L. Lengths and Periods
徐神纯在C,我题目都没看懂的字符串直接大力单切
本来好像要写SAM+线段树合并的,后面写了个启发式合并set
也淦过去了
#include <bits/stdc++.h>
using llsi = long long signed int;
constexpr int $n = 400000 + 5;
int go[$n][26], fa[$n], len[$n], las = 1, O = 1;
int insert(char a) {
int c = a - 'a', p = las;
if(go[p][c]) {
int q = go[p][c];
if(len[q] == len[p] + 1) return las = p;
int nq = ++O; len[nq] = len[p] + 1;
for(int i = 0; i < 26; ++i) go[nq][i] = go[q][i];
for(; p && go[p][c] == q; p = fa[p]) go[p][c] = nq;
fa[nq] = fa[q]; fa[q] = nq;
return las = nq;
}
int np = las = ++O;
len[np] = len[p] + 1;
for(int i = 0; i < 26; ++i) go[np][i] = 0;
for(; p && !go[p][c]; p = fa[p]) go[p][c] = np;
if(!p) return fa[np] = 1, las;
int q = go[p][c];
if(len[q] == len[p] + 1) return fa[np] = q, las;
int nq = ++O; len[nq] = len[p] + 1;
for(int i = 0; i < 26; ++i) go[nq][i] = go[q][i];
fa[nq] = fa[q]; fa[np] = fa[q] = nq;
for(; p && go[p][c] == q; p = fa[p]) go[p][c] = nq;
return las;
}
struct frac {
llsi a, b;
inline friend bool operator <(const frac &x, const frac &y) {
return x.a * y.b < y.a * x.b;
}
};
std::set<int> set_base[$n];
template<typename T>
void updmn(T &a, const T &b) {
if(b < a) a = b;
}
template<typename T>
void updmx(T &a, const T &b) {
if(a < b) a = b;
}
frac ans = frac{1, 1};
struct Msg {
std::set<int> *s;
int mindis;
inline void insert(const int &e) {
auto it = s->lower_bound(e + 1);
if(it != s->end()) updmn(mindis, *it - e);
it = s->lower_bound(e);
if(it != s->begin())
updmn(mindis, e - *--it);
s->insert(e);
}
} msg[$n];
Msg merge(Msg a, Msg b) {
if(a.s->size() < b.s->size()) std::swap(a, b);
updmn(a.mindis, b.mindis);
for(auto e: *(b.s)) a.insert(e);
return a;
}
std::string s;
std::vector<int> ch[$n];
void dfs(int now) {
for(auto ch: ch[now]) {
dfs(ch);
msg[now] = merge(msg[now], msg[ch]);
}
// std::cerr << "now = " << now << char(10);
// if(msg[now].s->size() > 1) {
// for(auto e: *(msg[now].s)) std::cerr << e << char(32);
// std::cerr << "mindis = " << msg[now].mindis << std::endl;
// }
if(msg[now].mindis <= s.size())
updmx(ans, frac{ len[now] + msg[now].mindis, msg[now].mindis });
}
int main() {
std::cin >> s;
for(int i = 0; i < s.size(); ++i) insert(s[i]);
for(int i = 1; i <= O; ++i) msg[i].s = set_base + i, msg[i].mindis = 0x7fffffff, ch[fa[i]].push_back(i);
for(int i = 0, now = 1; i < s.size(); ++i) {
now = go[now][s[i] - 'a'];
msg[now].insert(i);
}
dfs(1);
int g = std::__gcd(ans.a, ans.b);
std::cout << ans.a / g << '/' << ans.b / g << std::endl;
return 0;
}
14|0M. Managing Difficulties
签到,直接暴力枚举两项,用unordered_map
统计另一项个数即可
#include<bits/stdc++.h>
#define int long long
using namespace std;
//vector<int> hsh;
//int gethsh(int x){return lower_bound(hsh.begin(), hsh.end(), x)-hsh.begin()+1;}
const int N = 2e3+5;
int t, n, A[N];
signed main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> t;
while (t--){
cin >> n;
unordered_map<int, int> mp;
for (int i=1; i<=n; ++i){
cin >> A[i];
// hsh.push_back(A[i]);
}
// sort(hsh.begin(), hsh.end());
// hsh.erase(unique(hsh.begin(), hsh.end()), hsh.end());
int ans = 0;
for (int j=1; j<=n; ++j){
for (int k=j+1; k<=n; ++k){
if (A[j]==A[k]) continue;
int a = 2*A[j]-A[k];
ans += mp[a];
}
++mp[A[j]];
}
for (auto [a, c] : mp){
if (c>=3){
ans += c*(c-1)*(c-2)/6;
}
}
cout << ans << '\n';
}
return 0;
}
15|0Postscript
God Xu is our red sun, we can not live without him.
__EOF__

本文链接:https://www.cnblogs.com/cjjsb/p/17978206.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
2020-01-21 LOJ #2552. 「CTSC2018」假面
2019-01-21 UVA10838 The Pawn Chess
2019-01-21 UVA12298 Super Poker II