2019银川区域赛BDFGHIKN题解
B.So Easy
题目大意:给你一个正方形矩阵,初始都是0,题目对这个矩阵做了许多次操作,每次操作把行+1或列+1.其中有一个元素被隐藏了,你需要找出这个被隐藏的元素并判断它在操作之后应该是多少。
做法:我们设被隐藏的数的下标为\(x,y\)。再设另一个数的下标为\(x_0,y_0(i\not=i_0,j\not=j_0)\),再设对行加了\(\{r_i\}\),对列加了\(\{c_i\}\)。那么可以得到下面四个式子。
有了这四个式子接下来随便代一代消一消得到
代码里选择\((1,1)\) 或 \((n,n)\)作为\((x_0,y_0)\)
#include<cstdio>
using namespace std;
const int N = 1000 + 3;
int a[N][N];
int main() {
int n,x,y;
scanf("%d",&n);
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
scanf("%d",&a[i][j]);
if(a[i][j] == -1) x=i,y=j;
}
}
int x0 = 1, y0 = 1;
if(x == 1 && y == 1) x0 = y0 = n;
printf("%d\n",a[x0][y]+a[x][y0]-a[x0][y0]);
return 0;
}
D. Easy Problem
题目大意:给出n,m,d,k请求出
做法:莫比乌斯反演基础题。模拟赛的时候脑子抽了推成了别的东西。直接上式子。
推到这里就推完了。那么可以发现最后一项可以预处理,所以复杂度是\(O(m\log m)\)。log是快速幂的复杂度。
注意:在包含莫比乌斯函数的式子里,累加器可能加着加着就小于0了,记得把模数加上去。(找了两个小时的bugQAQ)。另外,这个模数不是质数,它的欧拉函数不是它减1.
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define IL inline
typedef long long LL;
const LL mod = 59964251;
const LL phim = 59870352;
const int N = 1e5 + 3;
int tot;
bool np[N];
int pri[N];
int mu[N];
IL void init_mu(int n) {
tot = 0;
mu[0] = 0;
np[1] = mu[1] = 1;
for(int i=2;i<=n;i++) {
if(!np[i]) { pri[tot++] = i; mu[i] = -1;}
for(int j=0;j<tot&&i*pri[j]<=n;j++) {
np[i*pri[j]] = true;
if(i%pri[j] == 0) {
mu[i*pri[j]] = 0;
break;
}
mu[i*pri[j]] = -mu[i];
}
}
}
IL LL ksm(LL a,LL b,LL p) {
LL res = 1;
while(b) {
if(b&1LL) res = res * a % p;
a = a * a % p;
b >>= 1LL;
}
return res;
}
LL n,m,d,k;
char s[N];
LL sumc[N];
int main() {
init_mu(1e5); int T; scanf("%d\n",&T);
while(T--) {
scanf("%s%lld%lld%lld\n",s,&m,&d,&k);
m /= d;
int len = strlen(s);
n = 0;
for(int i=0;i<len;i++) {
n = (n*10 + s[i] - '0') % phim;
}
sumc[0] = 0;
for(int i=1;i<=m;i++) sumc[i] = (sumc[i-1]+ksm(i,k,mod)) % mod;
LL ans = 0;
for(int i=1;i<=m;i++) {
ans = (ans + 1LL*mu[i]*ksm(i,n*k%phim+phim,mod)%mod*ksm(sumc[m/i],n+phim,mod)%mod + mod) % mod;
}
ans = ans * ksm(d,n*k%phim+phim,mod) % mod;
printf("%lld\n",ans);
}
return 0;
}
F. Function!
题目大意:给你n,求出$$\sum_{a=2}n(a\sum_{b=a}n \lfloor\log_ab\rfloor \lceil\log_ba\rceil)$$
做法:先看最右面一项,可以发现\(b>=a\),那么显然\(\lceil\log_ba\rceil=1\)。
那么式子化为$$\sum_{a=2}n(a\sum_{b=a}n \lfloor\log_ab\rfloor$$
接下来我们研究\(\lfloor\log_ab\rfloor\)这个函数的性质。
接下来分情况讨论。
当\(a>\sqrt n\) 时,大于\(\sqrt n\)部分的求和变为$$\sum_{a=\sqrt{n}+1}^n a(n+1)-a2=(n+1)\sum_{a=\sqrt{n}+1}n a - \sum_{a=\sqrt{n}+1}na2$$ 这个式子利用平方和公式和等差数列求和,可以\(O(1)\)计算。
当\(a<=\sqrt n\) 时,这里我们枚举\(\lfloor\log_ab\rfloor\)的大小去计算答案。
总时间复杂度为\(O(\sqrt n \log n)\)
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define IL inline
typedef long long LL;
const LL mod = 998244353;
const LL inv2 = 499122177;
const LL inv6 = 166374059;
LL n;
IL LL he(LL a,LL b) {
a %= mod; b %= mod;
return (b*(b+1)%mod*inv2%mod - a*(a+1)%mod*inv2%mod + mod) % mod;
}
IL LL he2(LL a,LL b) {
a %= mod; b %= mod;
return (b*(b+1)%mod*(2*b+1)%mod*inv6%mod - a*(a+1)%mod*(2*a+1)%mod*inv6%mod + mod) % mod;
}
int main() {
scanf("%lld",&n);
int m = sqrt(n+0.5);
LL sum1 = 0, sum2 = 0;
for(int a=2;a<=m;a++) {
LL b = a;
int logab = 1;
for(;b<=n;logab++,b*=a) {
sum1 = (sum1 + 1LL*a*logab%mod*(min(b*a-1,n)-b+1)%mod) % mod;
}
}
sum2 = ((1+n)%mod*he(m,n)%mod - he2(m,n)%mod + mod) % mod;
printf("%lld\n",(sum1+sum2)%mod);
return 0;
}
G. Pot!!
题目大意:定义一种函数叫做pot.它的意义是\(pot_p(n)=m\),当且仅当\(p^m|n,p^{m+1} \not|n\)。p是质数。给你一个序列,初始值全为1.
有两种操作:
- 区间乘。把[l,r]所有数乘x。
- 输出$$\max_{l<=i<=r} { \max_{p|a_i} { pot_p(a_i) } } (1<=l<=r<=n)$$
做法:我们可以发现这道题x非常小,2~10.所以我们可以发现这里的p只有2,3,5,7.所以我们可以建四棵线段树,分别代表区间2,3,5,7最大的次数。这样我们就把区间乘改成了区间加。
模拟赛的时候抄了刘汝佳的板子,还是要改天把自己的pushdown版本印出来备用。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define IL inline
typedef long long LL;
const int N = 1e5 + 3;
const int pri[] = {2,3,5,7};
const int ipri[] = {0,0,0,1,0,2,0,3};
struct SegmentTree {
int n;
int addv[4][N<<2];
int maxv[4][N<<2];
void build(int o,int L,int R) {
if(L == R) {maxv[0][o]=maxv[1][o]=maxv[2][o]=maxv[3][o]=0;return;}
int M = L + (R-L) / 2;
build(o<<1,L,M); build(o<<1|1,M+1,R);
for(int i=0;i<4;i++) maxv[i][o] = 0;
}
IL void init(int n){ this->n = n; build(1,1,n);}
IL void pushdown(int o,int L,int R) {
int M = L + (R-L) / 2;
for(int i=0;i<4;i++) {
maxv[i][o<<1] = maxv[i][o<<1] + addv[i][o];
maxv[i][o<<1|1] = maxv[i][o<<1|1] + addv[i][o];
addv[i][o<<1] = addv[i][o<<1] + addv[i][o];
addv[i][o<<1|1] = addv[i][o<<1|1] + addv[i][o];
addv[i][o] = 0;
}
}
int qL,qR,v[5];
void update(int o,int L,int R) {
if(qL <= L && R <= qR) {
for(int i=0;i<4;i++) {
maxv[i][o] += v[i];
addv[i][o] += v[i];
}
return;
}
pushdown(o,L,R);
int M = L + (R-L) / 2;
if(qL <= M) update(o<<1,L,M);
if(M < qR) update(o<<1|1,M+1,R);
for(int i=0;i<4;i++) {
maxv[i][o] = max(maxv[i][o<<1],maxv[i][o<<1|1]);
}
}
IL void upd(int L,int R,int x) {
v[0]=v[1]=v[2]=v[3] = 0;
for(int i=2;i<=x;i++) {
while(x % i == 0) {
v[ipri[i]]++;
x /= i;
}
}
qL = L; qR = R; update(1,1,n);
}
int pos;
int query(int o,int L,int R) {
if(qL <= L && R <= qR) return maxv[pos][o];
pushdown(o,L,R);
int M = L + (R-L) / 2, ans = 0;
if(qL <= M) ans = max(ans,query(o<<1,L,M));
if(M < qR) ans = max(ans,query(o<<1|1,M+1,R));
return ans;
}
IL int que(int L,int R) {
int ans = 0; qL = L; qR = R;
for(int i=0;i<4;i++) {
pos = i;
ans = max(ans,query(1,1,n));
}
return ans;
}
};
SegmentTree solver;
const int LEN = 20;
char s[LEN];
int main() {
int n,q;
scanf("%d%d\n",&n,&q);
solver.init(n);
while(q--) {
scanf("%s",s);
if(s[1] == 'U') { int l,r,x; scanf("%d%d%d\n",&l,&r,&x); solver.upd(l,r,x);}
else {
int l,r; scanf("%d%d\n",&l,&r);
printf("ANSWER %d\n",solver.que(l,r));
}
}
return 0;
}
H. Delivery Route
题目大意:给你一个无向图,无向图边权都为正。再给你一些有向边插进去,这些有向边可能边权为负。保证有向边之间不形成环,求源点到所有点的最短路。
做法:甚至出原题,甚至数据范围都不改。bzoj2200.
模拟赛的时候三个人各种换板子,上dij的3ms速度wa掉,上spfa的2000msT掉。
赛后写了一下spfa+slf的优化,过不去。在洛谷上交过了。
所以最后还是写了正解。
正解是先对无向图缩点,然后按照拓扑序去对这些点做dijkstra过程。而拓扑排序可以dfs也可以bfs,所以这里直接把拓扑排序和dijkstra结合在一起了。
注意:最后判断NO PATH的时候,不能判断\(d[i]>=INF\),因为有负权边,会更小,所以我直接写\(d[i]>=INF/2\)了...
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<algorithm>
using namespace std;
#define IL inline
typedef long long LL;
const int N = 25000 + 3;
const int INF = 1e9 + 3;
inline void read(int &x){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch&15);ch=getchar();}
x=s*w;
}
void print(int x){
if(x<0) {
putchar('-');
x=-x;
}
if(x>=10)print(x/10);
putchar(x%10+'0');
}
struct Edge {
int u,v,w;
Edge(int u=0,int v=0,int w=0):u(u),v(v),w(w){}
};
struct HeapNode {
int d,u;
IL bool operator < (const HeapNode& rhs) const {
return d > rhs.d;
}
IL HeapNode(int d,int u):d(d),u(u){}
};
struct Dijkstra {
int n,m;
int scc_cnt;
vector<Edge> edges;
vector<int> G[N];
bool done[N];
int d[N];
int ru[N];
int belong[N];
IL void init(int n) {
this->n = n; scc_cnt = 0;
for(int i=0;i<n;i++) G[i].clear();
edges.clear();
}
IL void AddEdge(int u,int v,int w) {
edges.push_back(Edge(u,v,w));
m = edges.size();
G[u].push_back(m-1);
}
IL void dfs(int u) {
for(int i=0;i<G[u].size();i++) {
Edge &e = edges[G[u][i]];
int v = e.v;
if(belong[v]) continue;
belong[v] = belong[u];
dfs(v);
}
}
IL void dijkstra(int s) {
for(int i=0;i<n;i++) d[i] = INF;
d[s] = 0;
memset(done,0,sizeof(done));
priority_queue<HeapNode> q;
queue<int> sccq;
sccq.push(belong[s]);
for(int i=1;i<=scc_cnt;i++)
if(!ru[i]) sccq.push(i);
while(!sccq.empty()) {
int sccu = sccq.front(); sccq.pop();
for(int i=0;i<n;i++) {
if(belong[i]==sccu) q.push(HeapNode(d[i],i));
}
while(!q.empty()) {
HeapNode x = q.top(); q.pop();
int u = x.u;
if(done[u]) continue;
done[u] = true;
for(int i=0;i<G[u].size();i++) {
Edge &e = edges[G[u][i]];
int v = e.v, w = e.w;
if(d[v] > d[u] + w) {
d[v] = d[u] + w;
if(belong[u]==belong[v]) q.push(HeapNode(d[v],v));
}
if(belong[u]!=belong[v]&&--ru[belong[v]]==0) sccq.push(belong[v]);
}
}
}
}
IL void print() {
for(int i=0;i<n;i++) {
if(d[i] >= INF/2) printf("NO PATH\n");
else printf("%d\n",d[i]);
}
}
};
Dijkstra solver;
int main() {
//freopen("H.in","r",stdin);
//freopen("H.out","w",stdout);
int n,x,y,s;
read(n); read(x); read(y); read(s); s--;
solver.init(n);
for(int i=0;i<x;i++) {
int u,v,w; read(u); read(v); read(w); u--; v--;
solver.AddEdge(u,v,w);
solver.AddEdge(v,u,w);
}
for(int i=0;i<n;i++) {
if(!solver.belong[i]) { solver.belong[i]=++solver.scc_cnt; solver.dfs(i);}
}
for(int i=0;i<y;i++) {
int u,v,w; read(u); read(v); read(w); u--; v--;
solver.AddEdge(u,v,w);
solver.ru[solver.belong[v]]++;
}
solver.dijkstra(s);
solver.print();
return 0;
}
I. Base62
题目大意:进制转换
做法:我也不会,反正队友会就行了!(理直气壮)(以前听学长说他们同届的只会学习一点写代码能力的学怪连进制转换都不会写,我大声地嘲讽了他们。现在发现,原来我也不会。)T.T
贴上我照着队友的代码补题的代码。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define IL inline
typedef long long LL;
const int N = 120 + 3;
int x,y;
char z[N];
int a[N];
int ans[N<<3];
IL int id(char c) {
if('0'<=c&&c<='9') return c - '0';
if('A'<=c&&c<='Z') return c - 'A' + 10;
if('a'<=c&&c<='z') return c - 'a' + 36;
}
int main() {
scanf("%d%d%s",&x,&y,z);
int len = strlen(z);
for(int i=0;i<len;i++) a[i] = id(z[i]);
int now = 0;
for(int i=0;i<len; ) {
for(int j=i;j<len-1;j++) {
a[j+1] += a[j] % y * x;
a[j] /= y;
}
ans[now++] = a[len-1] % y;
a[len-1] /= y;
while(i<len&&a[i]==0) i++;
}
for(int i=0;i<now;i++) {
int u = ans[now-i-1];
if(u <= 9) putchar(u+'0');
else if(u <= 35) putchar(u-10+'A');
else putchar(u-36+'a');
}
return 0;
}
K. Largest Common Submatrix
题目大意:给你两个nm矩阵,你要找出它们最大相同子矩阵。每个矩阵的元素的值从1~nm
做法:由于两个矩阵都是 1 到 nm 的排列,可以先做一个映射使得其中一个矩阵从上往下从左往右依次是 1 到 nm。这一步可参考最长公共子序列那个题的\(O(n \log n)\) 做法的第一步。
对于另一个矩阵的某个位置,如果左侧比它小 1,则认为左侧与它同色,如果上边比它小 m,则认为上边与它同色。
悬线法计算最大同色子矩阵即可。可以参考uvalive3029.
队友用神奇的做法A了这题,我也不知道是怎么A的。反正就是A了,我也懒得再写一遍悬线法。
把队友的代码丢上来。(做法与代码无关QWQ)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5, M = 1005;
struct Point {
int v, x, y;
}a[N], b[N], stck[M];
int A[M][M], B[M][M], t[M][M];
int top, ans;
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &A[i][j]), a[A[i][j]].x = i, a[A[i][j]].y = j;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &B[i][j]), b[B[i][j]].x = i, b[B[i][j]].y = j;
for (int j = 1; j <= m; j++)
t[n][j] = 1;
for (int i = n - 1; i >= 1; i--) {
for (int j = 1; j <= m; j++)
if (A[i + 1][j] == B[b[A[i][j]].x + 1][b[A[i][j]].y])
t[i][j] = t[i + 1][j] + 1;
else t[i][j] = 1;
}
for (int i = 1; i <= n; i++) {
stck[++top] = (Point) {t[i][1], 1, 1};
ans = max(ans, t[i][1]);
for (int j = 2; j <= m; j++) {
int lstv = A[i][j - 1];
if (A[i][j] == B[b[lstv].x][b[lstv].y + 1]) {
Point tmp = (Point) {t[i][j], j, j};
while (top && stck[top].v >= t[i][j]) {
Point now = stck[top];
tmp.x = now.x;
ans = max(ans, now.v * (j - now.x));
top--;
}
stck[++top] = tmp;
}
else {
while (top) {
Point now = stck[top];
ans = max(ans, now.v * (j - now.x));
top--;
}
top = 0;
stck[++top] = (Point) {t[i][j], j, j};
ans = max(ans, t[i][j]);
}
}
while (top) {
Point now = stck[top];
ans = max(ans, now.v * (m - now.x + 1));
top--;
}
}
printf("%d", ans);
return 0;
}
N. Fibonacci Sequence
题目大意:1 1 2 3 5
做法:1 1 2 3 5
#include<cstdio>
int main() {
printf("1 1 2 3 5\n");
return 0;
}
你看这样就AC了8题,在银川,8题可是能出线啊!(震声)