2019 ICPC Asia Yinchuan Regional 部分题解

传送门 

G 题

题目 :

p^m|n 、并且 p 是素数, 定义成 pot_p(n) = m

初始ai = 1, 有两种操作:

  1. 区间乘,将区间 (l,r)乘 x , (2 <= x <= 10)
  2. 区间查询

解法:

查询的是区间内 素数的最大幂数

对 2,3,5,7 分别维护一颗线段树,区间加、区间取 max 即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int n, q;
char s[10];
int a[10] = {2, 3, 5, 7};
int table[12][10];
void init(){
	for(int i = 2; i <= 10; i++){
		int tmp = i;
		for(int j = 0; j < 4; j++){
			int cnt = 0;
			while(tmp%a[j] == 0){
				cnt++;
				tmp /= a[j];
			}
			table[i][j] = cnt;
		}
	}
}
int Max[N*4][4], lazy[N*4][4];

void push(int o){
	for(int i = 0; i < 4; i++){
		if(lazy[o][i]){
			int tmp = lazy[o][i];
			lazy[o<<1][i] += tmp;
			Max[o<<1][i] += tmp;
			lazy[o<<1|1][i] += tmp;
			Max[o<<1|1][i] += tmp;
			lazy[o][i] = 0;
		}
	}
}

void up(int o, int l, int r, int ql, int qr, int pos, int v){
	if(ql <= l && qr >= r){
		Max[o][pos] += v;
		lazy[o][pos] += v;
		return ;
	}
	push(o);
	int mid = l+r >> 1;
	if(ql <= mid) up(o<<1, l, mid, ql, qr, pos, v);
	if(qr > mid) up(o<<1|1, mid+1, r, ql ,qr, pos, v);
	for(int i = 0; i < 4; i++){
		Max[o][i] = max(Max[o<<1][i], Max[o<<1|1][i]);
	}
}

int qu(int o, int l, int r, int ql, int qr){
	if(ql <= l && qr >= r){
		int res = 0;
		for(int i = 0; i < 4; i++){
			res = max(res, Max[o][i]);
		}
		return res;
	}
	push(o);
	int res = 0;
	int mid = l+r>>1;
	if(ql <= mid) res = max(res, qu(o<<1, l, mid, ql, qr));
	if(qr > mid) res = max(res, qu(o<<1|1, mid+1, r, ql, qr));
	for(int i = 0; i < 4; i++){
		Max[o][i] = max(Max[o<<1][i], Max[o<<1|1][i]);
	}
	return res;
}

void pika(int l, int r, int w){
	for(int i = 0; i < 4; i++){
		if(table[w][i]){
			up(1, 1, n, l, r, i, table[w][i]);
		}
	}
}

int main(){
	scanf("%d%d", &n, &q);
	init();
	int l, r , w;
	for(int i = 0; i < q; i++){
		scanf("%s", s);
		if(s[1] == 'U'){
			scanf("%d%d%d", &l, &r, &w);
			pika(l, r, w);
		}
		else{
			scanf("%d%d", &l, &r);
			int ans = qu(1, 1, n, l, r);
			printf("ANSWER %d\n", ans);
		}
	}
	return 0;
}

H题

spfa + slf 容错优化

SLF优化:将原队列改成双端队列,对要加入队列的点 v,如果 d[v] 小于队头元素d[q.front()],将其插入到队头,否则插入到队尾。

选择合适的容错值  sqrt(sum)/800 ~ sqrt(sum)/50

LLL优化:对每个要出队的队头元素 u,比较 dist[u] 和队列中点的 dist 的平均值,如果 dist[u] 更大,将其弹出放到队尾,然后取队首元素进行相同操作,直到队头元素的 dist 小于等于平均值。

这题SLF + LLL优化会T,SLF + 容错优化可以过。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e4;
const int M = 2e6;
const ll INF = 1e18;
int n, x, y, s;
struct Edge{
	int to, next, w;
}e[M];
int head[N], tot = 0;
void addEdge(int u, int v, int w){
	e[tot] = Edge{v, head[u], w};
	head[u] = tot++;
}

ll d[N];
int vis[N];
ll val;
int q[M], l, r;
void spfa(){
	for(int i = 1; i <= n; i++){
		d[i] = INF;
	}
	d[s] = 0;
	vis[s] = 1;
	l = r = 1e6;
	q[l] = s;
	while(l <= r){
		int u = q[l];
		l++;
		vis[u] = 0;
		for(int i = head[u]; i != -1; i = e[i].next){
			int v = e[i].to;
			if(d[v] > d[u] + e[i].w){
				d[v] = d[u] + e[i].w;
				if(!vis[v]){
					if(l > r || d[v] <= d[q[l]] + val){
						q[--l] = v;
					}
					else{
						q[++r] = v;
					}
					vis[v] = 1;
				}
			}
		}
	}
}

int main(){
	scanf("%d%d%d%d", &n, &x, &y, &s);
	memset(head, -1, sizeof(int)*(n+1));
	int u, v, w;
	for(int i = 0; i < x; i++)	{
		scanf("%d%d%d", &u, &v, &w);
		addEdge(u, v, w);
		addEdge(v, u, w);
		val += 2*w;
	}

	for(int i = 0; i < y; i++)	{
		scanf("%d%d%d", &u, &v, &w);
		addEdge(u, v, w);
		val += w;
	}
	val = sqrt(val)/300;
	spfa();
	for(int i = 1; i <= n; i++){
		if(d[i] == INF) printf("NO PATH\n");
		else printf("%lld\n", d[i]);
	}
	return 0;
}

F 题

\sum _{a=2}^{n}a\sum _{b=a}^{n}\left \lfloor log_{a}b \right \rfloor\left \lceil log_{b}a \right \rceil \ \ \ (n <= 10^{12})

因为b >= a,所以 \left \lceil log_{b} \ a \right \rceil = 1 , 公式可以变为

\sum _{a=2}^{n}a\sum _{b=a}^{n}\left \lfloor log_{a}b \right \rfloor

当 a <= \sqrt{n} 时, 可以用分块算出答案, 当 a > \sqrt{n} 时,\left \lfloor log_{a}b \right \rfloor = 1,公式可以变为

\sum _{a=\sqrt{n}}^{n}a *(n-a+1) = \sum _{a=\sqrt{n}}^{n} a*(n+1)-\sum _{a=\sqrt{n} }^{n} a^2

\sum _{a=1}^{n} a*(n+1) =\frac{ (1+n)*n}{2}*(n+1)  

\sum _{a=1 }^{n} a^2 = \frac{n *(n+1)*(2*n+1)}{6}

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
ll n;

ll qpow(ll a,int x){
	ll res = 1;
	while(x){
		if(x&1) res = res * a % mod;
		a = a * a % mod;
		x /= 2;
	}
	return res;
}
ll get(ll x){
	x %= mod;
	return ((1+x)*x%mod*(n%mod+1)%mod*qpow(2,mod-2)%mod - x%mod*(x+1)%mod*(2*x+1)%mod*qpow(6,mod-2)%mod + mod)%mod;
}

int main(){
	scanf("%lld", &n);
	ll mx = sqrt(n)+1;
	ll ans = 0;
	for(int i = 2; i <= mx; i++){
		ll k = i;
		ll cnt = 1;
		for(ll j = i; j <= n; j = k){
			k *= i;
			if(k <= n+1){
				ans = (ans + (k-j)%mod*i%mod*cnt) % mod;
			}
			else{
				ans = (ans + (n-j+1)%mod*i%mod*cnt) % mod;
			}
			cnt++;
		}
	}
	ans = (ans + get(n) - get(mx) + mod)%mod;
	printf("%lld\n", ans);
	return 0;
}

K 题

首先处理好每个 aij 上、左、右对应的数字,然后就是一个悬线法模板题了。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e3+10;

int n, m;
int a[N][N];
int p[4][N*N]; // 0上  1左  2右 
int u[N][N], l[N][N], r[N][N];
int main(){
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			scanf("%d", &a[i][j]);
		}
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			p[0][a[i][j]] = a[i-1][j];
			p[1][a[i][j]] = a[i][j-1];
			p[2][a[i][j]] = a[i][j+1];
		}
	}
	
	for(int i = 1;i <= n; i++){
		for(int j = 1; j <= m; j++){
			scanf("%d", &a[i][j]);
		}
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			if(p[0][a[i][j]] == a[i-1][j]){
				u[i][j] = u[i-1][j] + 1;
			}
			else u[i][j] = 1;
			if(p[1][a[i][j]] == a[i][j-1]){
				l[i][j] = l[i][j-1] + 1;
			}
			else l[i][j] = 1;
		}
		for(int j = m; j >= 1; j--){
			if(p[2][a[i][j]] == a[i][j+1]){
				r[i][j] = r[i][j+1] + 1;
			}
			else r[i][j] = 1;
		}
	}
	int mx = 0;
	for(int i = 2; i <= n; i++){
		for(int j = 1; j <= m; j++){
			if(p[0][a[i][j]] == a[i-1][j]){
				l[i][j] = min(l[i][j], l[i-1][j]);
				r[i][j] = min(r[i][j], r[i-1][j]);
			}
			if(mx < (l[i][j] + r[i][j] - 1) * u[i][j]){
				mx = (l[i][j] + r[i][j] - 1) * u[i][j];
			}
		}
	}
	printf("%d\n", mx);
	return 0;
}

I 题

Java、Python练习题,注意特判0

Python ord() 函数获取字符ASCII码表对应的值。 chr()将值转化为对应字符

list = input().split()
x = int(list[0], 10)
y = int(list[1], 10)
zz = list[2]   
z = 0
for i in zz:
	z = z * x;
	if i <= '9':
		z += ord(i) - ord('0')
	elif i <= 'Z':
		z += ord(i) - ord('A') + 10
	else :
		z += ord(i) - ord('a') + 36
s = ''
while z != 0:
	tmp = z % y
	if tmp < 10:
		s += chr(tmp + ord('0'))
	elif tmp < 36:
		s += chr(tmp - 10 + ord('A'))
	else :
		s += chr(tmp - 36 + ord('a'))
	z //= y
ans = ''
for i in range(len(s)-1, -1, -1):
	ans += s[i]
if len(ans) == 0:
	ans += '0'
print(ans)
import java.math.BigInteger;
import java.util.Scanner;

public class Main {

    static long getValue(char c){
        if(c <= '9' && c >= '0'){
            return c - '0';
        }
        else if(c <= 'z' && c >= 'a'){
            return c - 'a' + 36;
        }
        else{
            return c - 'A' + 10;
        }
    }

    static char getChar(String s){
        int tmp = 0;
        for(int i = 0; i < s.length(); i++){
            tmp = tmp * 10 + s.charAt(i) - '0';
        }
        if(tmp < 10){
            return (char)('0' + tmp);
        }
        else if(tmp < 36){
            return (char)('A' + tmp - 10);
        }
        else{
            return (char)('a' + tmp - 36);
        }
    }

    public static void main(String[] args) {
        BigInteger x;
        BigInteger y;
        String s;
        Scanner sc = new Scanner(System.in);
        x = sc.nextBigInteger();
        y = sc.nextBigInteger();
        s = sc.nextLine();
        BigInteger a = new BigInteger("0");
        BigInteger zero = new BigInteger("0");
        BigInteger tmp;
        for(int i = 1; i < s.length(); i++){
            tmp = BigInteger.valueOf(getValue(s.charAt(i)));
            a = a.multiply(x).add(tmp);
        }
        String ans = "";
        while(!a.equals(zero)) {
            tmp = a.mod(y);
            char c = getChar(tmp.toString());
            ans += c;
            a = a.divide(y);
        }
        for(int i = ans.length()-1; i >= 0; i--){
            System.out.print(ans.charAt(i));
        }
        if(ans.length() == 0) System.out.print("0");
        System.out.println("");
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值