ICPC 2019-2020 North-Western Russia Regional Contest

Preface

经过休整后终于等到一次三人合训,然后经典徐神带飞

感觉最近要过年了大家有空的时间都变少了,后面一起训练的机会可能就不多了,正好滚去刷一下之前欠下的CF


A. 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;
}

B. Bad Treap

首先这题很容易想到通过构造一个序列\(\{x_i\}\),使得其本身以及\(\sin x_i\)都单调

手玩一下会发现如果我们找到一个\(x_0\),满足\(\sin x_0>0\)\(\sin x_0\)的值尽可能小,那么我们只需要形如\(x_0,2x_0,3x_0,\cdots\)构造即可

写个程序找出的数是\(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)

C. 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;
}

D. Double Palindrome

比赛的时候队友已经给出了去重的关键结论了,但可惜最后统计的时候状态没设计好变得很繁琐导致没能做出这题

首先考虑设\(f_x\)表示不考虑重复时长度为\(x\)的双回文串的方案数,我们暴力枚举其中一个回文串的长度得到式子:

\[f_x=\sum_{i=0}^{x-1} k^{\lceil\frac{i}{2}\rceil}\times k^{\lceil\frac{n-i}{2}\rceil} \]

现在的问题就是当存在某个串\(S\)可以拆分成多个回文串时上述的方法会算重,但手玩一下会发现若某个串的划分方式不唯一,则它一定可以写作\(m\)\(s\)的形式,其中\(s\)是一个划分方式唯一的串

根据上面\(f_x\)的计算式会发现这个串恰好被计算了\(m\)次,因此可以容斥得到划分方案唯一的双回文串个数\(g(x)\)

\[g_x=f_x-\sum_{y|x,y\ne x} \frac{x}{y}\times g_y \]

最后的答案就是\(\sum_{i=1}^ n \lfloor\frac{n}{i}\rfloor\times g_i\),总复杂度\(O(n\log n)\)

#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;
}

E. 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;
}

F. Foreach

防AK题,策不来一点


G. Golf Time

防AK题连坐


H. High Load Database

注意到\(\sum a_i\le 10^6\),因此很容易想到根号分治,设阈值\(S\)

  • \(t_i\le S\)时,本质不同的询问只有\(S\)种,可以直接\(O(n)\)跑一遍贪心求解
  • \(t_i>S\)时,每次二分找位置,需要跳的次数为\(\frac{\sum a_i}{S}\)

tradeoff一下得到当\(S=\sqrt {\sum a_i\times \log n}\)时,最优复杂度为\(O(q\times 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;
}

I. 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;	
}

J. Just the Last Digit

想到突破口后就很简单的构造题

不难发现\(i\to i+1\)的路径条数就反应了两点间是否有边,因此可以先推出所有相距为\(1\)的点对连边关系

然后考虑诸如\(i\to i+2\)的路径条数,由于\(i\to i+1,i+1\to i+2\)已经知道,可以算出路径数量然后根据误差判断是否要加上\(i\to i+2\)的边

依此类推即可,总复杂度\(O(n^3)\)

#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;	
}

K. 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;
}

L. 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;
}

M. 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;	
}

Postscript

God Xu is our red sun, we can not live without him.

posted @ 2024-01-21 19:26  空気力学の詩  阅读(56)  评论(0编辑  收藏  举报