ICPC 2019-2020 North-Western Russia Regional Contest 部分题解

比赛链接

A - Accurate Movement

温暖的签到题。

#include 
using namespace std;
int a, b, n;
int main()
{
    scanf("%d%d%d", &a, &b, &n);
    int x = 0, y = 0, cnt = 0;
    while(x+b!=n||y+a!=n)
    {
        y = min(n-a, x + b - a); 
        ++cnt;
        if(x+b==n&&y+a==n) break;
        x = min(n-b, y);
        ++cnt;
    }
    printf("%d\n", cnt);
    return 0;
}

 

B - Bad Treap

考虑在一个函数的单调的半周期上寻找一个上升的序列,且要求尽可能小。寻找一个最接近极值的即可。

#include 
using namespace std;
const double pi = acos(-1.0);
int x[50005];
int main()
{
    x[1] = 11;
    for(int i=2; i<=50000; i++) x[i] = x[i-1] + 710;
    reverse(x+1, x+50001);
    int n; scanf("%d", &n);
    for(int i=1; i<=n; i++) printf("%d\n", x[i]);
    return 0;
}

 

C - Cross-Stitch

考虑对于每个,连四条边,左上角-左下角,左上角-右下角,右上角-左下角,右上角-右下角。求欧拉回路即可。

注意欧拉回路上的边要求斜边和竖直边交错。输出时只输出去掉一条竖直边的欧拉路即可。

#include
using namespace std ;
const int maxn = 100 + 10 ;
int n , m ;
char s[maxn][maxn] ;
int id(int x , int y)
{
    return x * (m + 1) + y + 1 ;
}
struct Link
{
    int num , head[2][maxn * maxn] ;
    bool vis[maxn * maxn * 20] ;
    vector> ans ;
    struct Edge
    {
        int v , w , next ;
    } edge[2][maxn * maxn * 20] ;
    void init()
    {
        num = 0 ;
        memset(head , 0 , sizeof(head)) ;
    }
    void add_edge(int u , int v , int w , int op)
    {
        num ++ ;
        edge[op][num].v = v ;
        edge[op][num].w = w ;
        edge[op][num].next = head[op][u] ;
        head[op][u] = num ;  
    } 
    void add(int u , int v , int w , int op)
    {
        add_edge(u , v , w , op) ;
        add_edge(v , u , -w , op) ;
    }
    void dfs(int u , int op)
    {
        //cout << op << ' ' << u << ' ' << head[op][u] << '\n' ;
        for(int &i = head[op][u] ; i != 0 ; i = edge[op][i].next) //需要引用,自动删边
        {
            int v = edge[op][i].v ;
            int w = edge[op][i].w ;
            if(vis[abs(w)])  continue ;
            vis[abs(w)] = true ;
            dfs(v , 1 - op) ;
            ans.push_back({u , v}) ;
        }
    }
    void print()
    {
        int siz = ans.size() ;
        reverse(ans.begin() , ans.end()) ;
        bool flag = false ;
        cout << siz - 1 << '\n' ;
        for(int i = 0 ; i < siz - 1 ; i ++)
        {
            int u = ans[i].first ;
            int v = ans[i].second ;
            if(!flag)
            {
                flag = true ;
                int ux = (u - 1) / (m + 1) ;
                int uy = (u - 1) % (m + 1) ;
                int vx = (v - 1) / (m + 1) ;
                int vy = (v - 1) % (m + 1) ;
                cout << uy << ' ' << ux << '\n' ;
                cout << vy << ' ' << vx << '\n' ;
            }
            else
            {
                int vx = (v - 1) / (m + 1) ;
                int vy = (v - 1) % (m + 1) ;
                cout << vy << ' ' << vx << '\n' ;
            }
        }
    }
} link ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    cin >> m >> n ;
    for(int i = 1 ; i <= n ; i ++)  cin >> s[i] + 1 ;
    int cnt = 0 ;
    link.init() ;
    for(int i = 1 ; i <= n ; i ++)  for(int j = 1 ; j <= m ; j ++)
        if(s[i][j] == 'X')
        {
            link.add(id(i , j) , id(i - 1 , j - 1) , ++ cnt , 0) ;
            link.add(id(i , j - 1) , id(i - 1 , j) , ++ cnt , 0) ;
            link.add(id(i - 1 , j - 1) , id(i , j - 1) , ++ cnt , 1) ;
            link.add(id(i - 1 , j) , id(i , j) , ++ cnt , 1) ;
        }
    for(int i = 1 ; i <= n ; i ++)  for(int j = 1 ; j <= m ; j ++)
        if(s[i][j] == 'X')
        {
            link.dfs(id(i , j) , 0) ;
            link.print() ;
            return 0 ;
        }
    return 0 ;
}

 

D - Double Palindrome

好题。思路见注释。

#include
using namespace std ;
const int mod = 998244353 ;
long long qpow(long long a , long long b) //快速幂
{
    if(b < 0)  return 0 ;
    long long ans = 1 ; 
    a %= mod ;
    while(b)
    {
        if(b & 1)  ans = (ans * a) % mod ;
        b >>= 1 , a = (a * a) % mod ;
    }
    return ans % mod ;
}
int main()
{
    //需要发现一个现象,如果s可以划分为多种双回文串,即s=s1+s2=s3+s4,其中s1,s2,s3,s4都是回文串。
    //那么s=t+t+t+t+t+t+t,其中t是划分唯一的双回文串

    //f[i]表示长度是i的双回文串的划分个数
    //暴力算就好了

    //g[i]表示长度是i的划分唯一的字符串个数
    //g[i] = f[i] - i / j * g[j] , i % j = 0

    //通过g[i]暴力算答案
    //ans = \sigma_i=1^n g[i] * (n / i)

    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n , k ;
    cin >> n >> k ;
    vector f(n + 1 , 0) ;
    vector g(n + 1 , 0) ;
    for(int i = 1 ; i <= n ; i ++)
    {
        if(i % 2 == 1)  f[i] = i * qpow(k , (i + 1) / 2) ;
        else  f[i] = (i / 2) * qpow(k , i / 2) + (i / 2) * qpow(k , i / 2 + 1) ;
    }
    for(int i = 1 ; i <= n ; i ++)  g[i] = f[i] ;
    for(int j = 1 ; j <= n ; j ++)
    {
        for(int i = j + j ; i <= n ; i += j)
        {
            g[i] -= i / j * g[j] % mod ;
            g[i] %= mod ;
        }
    }
    long long ans = 0 ;
    for(int i = 1 ; i <= n ; i ++)  ans += 1ll * n / i * g[i] , ans %= mod ;
    ans = (ans + mod) % mod ;
    cout << ans << '\n' ;
    return 0 ;
}

 

E - Equidistant

换根计算每个点到关键点的路径长度的最大值和最小值,如果最大值和最小值相等,那么到每个关键点的路径全相等。

#include 
using namespace std;
const int N = 2e5 + 5, inf = 1e9;
int n, m;
vector G[N];
bool tag[N];
int mx[N], mn[N], mx2[N], mn2[N];
inline void Max(int &x, int y) { if(xy) x = y; }
void dfs(int u, int fa)
{
    mx[u] = -inf, mn[u] = inf;
    if(tag[u]) mn[u] = mx[u] = 0;
    for(int v : G[u])
    {
        if(v==fa) continue;
        dfs(v, u);
        Max(mx[u], mx[v]+1);
        Min(mn[u], mn[v]+1);
    }
}
void dfs2(int u, int fa)
{
    int smx = -inf, smx2 = -inf, smn = inf, smn2 = inf;
    for(int v : G[u])
    {
        if(v==fa) continue;
        if(mx[v]>smx) smx2 = smx, smx = mx[v];
        else if(mx[v]>smx2) smx2 = mx[v];
        if(mn[v]

 

F - Foreach

留坑。

 

G - Golf Time

留坑。

 

H - High Load Database

根号分治即可,注意划分块的大小要合理。

#include 
using namespace std;
const int N = 2e5 + 5, B = 600;
int n, q, a[N], sum[N];
int nxt[N][B], ans[B];
inline void read(int &x)
{
    x = 0;
    char c = getchar();
    while(c>'9'||c<'0') c = getchar();
    while(c>='0'&&c<='9') x = x*10 + c - 48, c = getchar();
}
void init(int s)
{
    int j = 1;
    for(int i=1; i<=n; i++)
    {
        while(j<=n && sum[j]-sum[i-1]<=s) ++j;
        nxt[i][s] = j; 
    }
    int p = 1, cnt = 0;
    while(p<=n)
    {
        p = nxt[p][s];
        ++cnt;
    }
    ans[s] = cnt;
}
inline int jump(int p, int t) { return upper_bound(sum+p, sum+n+1, sum[p-1]+t) - sum; }
int main()
{
    read(n);
    int mx = 0;
    for(int i=1; i<=n; i++) read(a[i]), sum[i] = sum[i-1] + a[i], mx = max(mx, a[i]);
    for(int i=mx; i<=B; i++) init(i);
    read(q);
    while(q--)
    {
        int t; read(t);
        if(t

 

I - Ideal Pyramid

二分金字塔的高度,考虑如何。

容易发现对于一个固定的金字塔的高度和一个固定的柱子,可行的金字塔中心的区域是正方形。原因是金字塔的等高线向地面的投影是正方形。

正方形的交集包含至少一个整数点即可。

#include
using namespace std;
#define M 1005
struct node{
	int x,y,h;
}s[M];
int xl,yl,xr,yr;
bool In(int x,int y){
	return x>=xl&&x<=xr&&y>=yl&&y<=yr; 
}
int n;
node check(int H){
	xl=-1e8,yl=-1e8,xr=1e8,yr=1e8;
	for(int i=1;i<=n;i++){
		int r=(H-s[i].h);
		int x1=s[i].x-r,y1=s[i].y-r;
		int x2=s[i].x+r,y2=s[i].y+r;
		if(x1<=xl&&x2>=xr||y1<=yl&&y2>=yr){
			swap(x1,xl);
			swap(x2,xr);
			swap(y1,yl);
			swap(y2,yr);
		}
		if(In(x1,y1)&&In(x2,y2)){
			xl=x1;yl=y1;
			xr=x2;yr=y2;
		}
		else if(In(x1,y1)&&In(x1,y2)){
			xl=x1;
			yl=y1;yr=y2;
		}
		else if(In(x1,y1)&&In(x2,y1)){
			xl=x1;xr=x2;
			yl=y1;
		}
		else if(In(x2,y2)&&In(x1,y2)){
			xl=x1;xr=x2;
			yr=y2;
		}
		else if(In(x2,y2)&&In(x2,y1)){
			xr=x2;
			yl=y1;yr=y2;
		}
		else if(In(x1,y1)){
			xl=x1;yl=y1;
		}
		else if(In(x1,y2)){
			xl=x1;yr=y2;
		}
		else if(In(x2,y1)){
			xr=x2;yl=y1;
		}
		else if(In(x2,y2)){
			xr=x2;yr=y2;
		}
		else if(x1>=xl&&x2<=xr&&yl>=y1&&yr<=y2){
			xl=x1;
			xr=x2;
		}
		else if(y1>=yl&&y2<=yr&&xl>=x1&&xr<=x2){
			yl=y1;
			yr=y2;
		}
		else {
			return (node){0,0,-1};
		}
	}
	return (node){xl,yl,H};
}
int main(){
	scanf("%d",&n);
	int l=1,r=1e9;
	for(int i=1;i<=n;i++){
		scanf("%d %d %d",&s[i].x,&s[i].y,&s[i].h);
		l=max(l,s[i].h);
	}
	node res;
	while(l<=r){
		int mid=l+r>>1;
		node tmp=check(mid);
		if(tmp.h!=-1){
			res=tmp;
			r=mid-1;
		}
		else l=mid+1;
	}
	printf("%d %d %d\n",res.x,res.y,res.h);
	return 0;
}

 

J - Just the Last Digit

对于的路径,枚举的。直接冲!

#include
using namespace std;
#define M 505
char str[M][M];
int a[M][M],G[M][M];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%s",str[i]+1);
		for(int j=1;j<=n;j++)a[i][j]=str[i][j]-'0';
	}
	for(int l=1;l

 

K - King's Children

首先通过悬线法求出包含的最大子矩阵。然后对于其他部分,从上往下扫一遍即可。

#include 
using namespace std;
const int N = 1e3 + 5;
int n, m, a[N][N], ax, ay;
int up[N][N], dwn[N][N], l[N][N], r[N][N];
int dl[N], dr[N];
char s[N][N], t[N][N];
void work(int u, int d, int l, int r)
{
    if(u>d || l>r) return;
    int pre = u - 1;
    for(int i=u; i<=d; i++)
    {
        bool fl = 0;
        for(int j=l; j<=r; j++) if(isalpha(s[i][j])) fl = 1;
        if(!fl) continue;
        int p = l - 1;
        for(int j=l; j<=r; j++)
            if(isalpha(s[i][j]))
            {
                for(int k=p+1; k0; j--)
            if(a[i][j]&&a[i][j+1]) r[i][j] = r[i][j+1];
    dl[ax] = l[ax][ay], dr[ax] = r[ax][ay];
    for(int i=ax-1; i>=1; i--) 
    {
        dl[i] = max(dl[i+1], l[i][ay]);
        dr[i] = min(dr[i+1], r[i][ay]);
    }
    for(int i=ax+1; i<=n; i++)
    {
        dl[i] = max(dl[i-1], l[i][ay]);
        dr[i] = min(dr[i-1], r[i][ay]);
    }
    int ans = 1, L = ay, R = ay, U = ax, D = ax;
    for(int up=1; ax-up+1>=1; up++)
    {
        int x = ax - up + 1;
        if(!a[x][ay]) break;
        for(int dwn=1; ax+dwn-1<=n; dwn++)
        {
            int y = ax + dwn - 1;
            if(!a[y][ay]) break;
            int cl = max(dl[x], dl[y]), cr = min(dr[x], dr[y]);
            if((cr-cl+1)*(y-x+1)>ans) ans = (cr-cl+1)*(y-x+1), L = cl, R = cr, U = x, D = y;
        }
    }
    for(int i=U; i<=D; i++)
        for(int j=L; j<=R; j++) t[i][j] = 'a';
    t[ax][ay] = 'A';
    work(1, U-1, 1, m);
    work(U, D, 1, L-1);
    work(U, D, R+1, m);
    work(D+1, n, 1, m);
}
int main()
{
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; i++) scanf("%s", s[i]+1);
    for(int i=1; i<=n; i++) 
        for(int j=1; j<=m; j++) 
        {
            if(s[i][j]=='.'||s[i][j]=='A') a[i][j] = 1;
            else a[i][j] = 0;
            if(s[i][j]=='A') ax = i, ay = j;
        }
    gao();
    for(int i=1; i<=n; i++) printf("%s\n", t[i]+1);
    return 0;
}

 

L - Lengths and Periods

这个题就是的强化了一点点的版本。

唯一区别就是用数组特判一下的部分即可。

#include
using namespace std ;
const int maxn = 2e5 + 10 ;
struct Sa //如果是多个测例,记得清空 s
{
    int rk[maxn << 1] , sa[maxn << 1] , height[maxn << 1] ;
    int tmp[maxn << 1] , cnt[maxn] ;
    char s[maxn] ;
    int st[maxn][25] ;
    void cal(int n , int m)
    {
        n ++ ;
        for(int i = 0 ; i < n * 2 + 5 ; i ++)
        rk[i] = sa[i] = height[i] = tmp[i] = 0 ;//开2 倍空间
        for(int i = 0 ; i < m ; i ++)  cnt[i] = 0 ;
        for(int i = 0 ; i < n ; i ++)  cnt[rk[i] = s[i]] ++ ;
        for(int i = 1 ; i < m ; i ++)  cnt[i] += cnt[i - 1] ;
        for(int i = 0 ; i < n ; i ++)  sa[-- cnt[rk[i]]] = i ;
        for(int k = 1 ; k <= n ; k <<= 1)
        {
            int j = 0 ;
            for(int i = 0 ; i < n ; i ++)
            {
                j = sa[i] - k ;
                if(j < 0)  j += n ;
                tmp[cnt[rk[j]] ++] = j ;
            }
            sa[tmp[cnt[0] = 0]] = j = 0 ;
            for(int i = 1 ; i < n ; i ++)
            {
                if(rk[tmp[i]] != rk[tmp[i - 1]]
                || rk[tmp[i] + k] != rk[tmp[i - 1] + k])
                cnt[++ j] = i ;
                sa[tmp[i]] = j ;
            }
            memcpy(rk , sa , n * sizeof(int)) ;
            memcpy(sa , tmp , n * sizeof(int)) ;
            if(j >= n - 1)  break ;
        }
        height[0] = 0 ;
        for(int i = 0 , k = 0 , j = rk[0] ; i < n - 1 ; i ++ , k ++)
        while(~k && s[i] != s[sa[j - 1] + k])
        height[j] = k -- , j = rk[sa[j] + 1] ;
    }   
    void build_lcp(int n)
    {
        for(int i = 1 ; i <= n ; i ++)  st[i][0] = height[i] ;
        for(int j = 1 ; j <= 20 ; j ++)
        for(int i = 1 ; i + (1 << j) - 1 <= n ; i ++)
        st[i][j] = min(st[i][j - 1] , st[i + (1 << (j - 1))][j - 1]) ;
    }
    int lcp(int l , int r)
    {
        if(l > r)  swap(l , r) ;
        l ++ ;
        int len = log2(r - l + 1) ;
        return min(st[l][len] , st[r - (1 << len) + 1][len]) ;
    }
} sa[2] ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    cin >> sa[0].s ;
    int len = strlen(sa[0].s) ;
    for(int i = 0 ; i < len ; i ++)  sa[1].s[i] = sa[0].s[len - 1 - i] ;
    sa[0].cal(len , 200) ;
    sa[0].build_lcp(len) ;
    sa[1].cal(len , 200) ;
    sa[1].build_lcp(len) ;
    auto rev = [&](int x)
    {
        return len - 1 - x ;
    } ;
    pair ans = {1 , 1} ;
    for(int i = 2 ; i <= len ; i ++)
    {
        int res1 = abs(sa[0].sa[i] - sa[0].sa[i - 1]) + sa[0].height[i] ;
        int res2 = abs(sa[0].sa[i] - sa[0].sa[i - 1]) ;
        if(1ll * res1 * ans.second > 1ll * res2 * ans.first)  ans = {res1 , res2} ;
    }
    for(int i = 1 ; i <= len ; i ++)
    {
        for(int j = 0 ; j < len ; j += i)
        {
            int p = j ;
            while(p + i < len && sa[0].lcp(sa[0].rk[p] , sa[0].rk[p + i]) >= i)  p += i ;
            if(p + i - 1 <= len - 1 && (p == j || sa[0].lcp(sa[0].rk[j] , sa[0].rk[p]) >= i))  p += i ;
            if((p - j) / i >= 1)
            {
                int x = p ;
                int lcp1 = 0 ;
                if(x < len)  lcp1 = sa[0].lcp(sa[0].rk[j] , sa[0].rk[x]) ;
                int y = j - 1 ;
                int lcp2 = 0 ;
                if(y >= 0)  lcp2 = sa[1].lcp(sa[1].rk[rev(j - 1)] , sa[1].rk[rev(j + i - 1)]) ;
                int res1 = lcp1 + lcp2 + p - j ;
                int res2 = i ;
                if(1ll * res1 * ans.second > 1ll * res2 * ans.first)  ans = {res1 , res2} ;
            }
            if(p > j)  j = p - i ;                
        }
    }
    int d = __gcd(ans.first , ans.second) ;
    ans.first /= d ;
    ans.second /= d ;
    cout << ans.first << '/' << ans.second << '\n' ;
    return 0 ;
}

 

M - Managing Difficulties

温暖的签到题。

#include
using namespace std;
#define M 2005
int a[M];
unordered_mapcnt;
void solve(){
    int n;
    scanf("%d",&n);
    int ans=0;
    cnt.clear();
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int j=n;j>=1;j--){
        for(int i=j-1;i>=1;i--){
            if(cnt.count(2*a[j]-a[i])) ans+=cnt[a[j]+a[j]-a[i]];
        }
        cnt[a[j]]++;
    }
    printf("%d\n",ans);
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--)solve(); 
    return 0;
} 

 

你可能感兴趣的:(ICPC 2019-2020 North-Western Russia Regional Contest 部分题解)