感觉这就是普及组的难度吧。仿佛看到了CSP-J2019自己的样子,一个小时切三题,然后剩下的时间死肝一题(不过这次好像一个半小时后肝出来了?
以下是十分简略的题解。。。
T1:1494. 密码
题目大意就是把n个不超过 1 0 24 10^{24} 1024的数乘起来。没必要压位。
#include
#include
using namespace std;
const int N = 2510;
char s[N];
int n,m,len,a[N],b[N];
template < class T >
inline void read(T &x)
{
char ch = getchar(); x = 0; int fg = 1;
for(;ch < '0' || ch > '9';) fg = ch == '-' ? -1 : 1, ch = getchar();
for(;ch >= '0' && ch <= '9';) x = x * 10 + (ch ^ '0'), ch = getchar(); x *= fg;
}
void mul(int a[N],int b[N])
{
int c[N]; memset(c,0,sizeof c);
for(int i = 1;i <= len; ++ i)
for(int j = 1;j <= m; ++ j)
c[i + j - 1] += a[i] * b[j], c[i + j] += c[i + j - 1] / 10, c[i + j - 1] %= 10;
for(int i = len + m + 1;i >= 1; -- i) if(c[i]) { len = i; break; }
memcpy(a,c,sizeof c);
}
void print() { for(int i = len;i; -- i) printf("%d",a[i]); }
int main()
{
read(n); a[1] = len = 1;
for(int i = 1;i <= n; ++ i)
{
scanf(" %s",s + 1), m = strlen(s + 1);
for(int i = 1;i <= m; ++ i) b[i] = s[m - i + 1] - '0';
mul(a,b);
} print();
return 0;
}
T2:1495. 宝石
题目大意:给出一个m * m的矩阵,求k * k的最大覆盖。
这种题一般都是前缀和或线段树维护扫描线。但是扫描线做过的题目并不多,好在算法本身简单,就考场手推了。听别人说没必要离散化,但是个人认为离散化并不难写,而且是一种好的习惯(这是在给自己没仔细算复杂度找借口嘛。。。
#include
#include
#include
using namespace std;
const int N = 100010;
template < class T >
inline void read(T &x)
{
char ch = getchar(); x = 0; int fg = 1;
for(;ch < '0' || ch > '9';) fg = ch == '-' ? -1 : 1, ch = getchar();
for(;ch >= '0' && ch <= '9';) x = x * 10 + (ch ^ '0'), ch = getchar(); x *= fg;
}
struct node{ int x,y,fg,val,id; } a[N],b[N];
int m,n,k,cur,cnt,L[N],R[N],ans = 0,t[N << 2],tag[N << 2];
int cmp(node a,node b) { return a.x < b.x; }
int cmp1(node a,node b) { return a.y < b.y || (a.y == b.y && a.val > b.val); }
#define ls p << 1
#define rs ls | 1
void push(int p,int tl,int tr)
{
if(!tag[p]) return;
int mid = tl + tr >> 1;
t[ls] += tag[p], t[rs] += tag[p];
tag[ls] += tag[p], tag[rs] += tag[p], tag[p] = 0;
}
int query(int p,int l,int r,int tl,int tr)
{
if(l <= tl && tr <= r) return t[p];
int mid = tl + tr >> 1,ret = 0; push(p,tl,tr);
if(mid >= l) ret = query(ls,l,r,tl,mid);
if(mid < r) ret = max(ret,query(rs,l,r,mid + 1,tr));
t[p] = max(t[ls],t[rs]); return ret;
}
void change(int p,int k,int l,int r,int tl,int tr)
{
if(l <= tl && tr <= r) { t[p] += k, tag[p] += k; return; }
int mid = tl + tr >> 1; push(p,tl,tr);
if(mid >= l) change(ls,k,l,r,tl,mid);
if(mid < r) change(rs,k,l,r,mid + 1,tr);
t[p] = max(t[ls],t[rs]);
}
int main()
{
read(m), read(n), read(k);
for(int i = 1;i <= n; ++ i)
read(a[i].x), read(a[i].y), read(a[i].val), a[i].fg = 1, a[i].id = i, a[i + n] = (node){ a[i].x + k,a[i].y,-1,a[i].val,i },
b[i].fg = 1, b[i].y = a[i].y, b[i].id = i, b[i].val = a[i].val, b[i + n] = (node){ b[i].x,b[i].y + k,-1,-b[i].val,b[i].id };
sort(a + 1,a + 1 + 2 * n,cmp);
for(int i = 1;i <= 2 * n; ++ i)
{
if(a[i].x != cur) ++cnt, cur = a[i].x;
if(a[i].fg == 1) L[a[i].id] = cnt; else R[a[i].id] = cnt;
}
sort(b + 1,b + 1 + 2 * n,cmp1);
for(int i = 1;i <= 2 * n; ++ i)
{
if(b[i].fg == 1) ans = max(ans,query(1,L[b[i].id],R[b[i].id],1,cnt) + b[i].val);
change(1,b[i].val,L[b[i].id],R[b[i].id],1,cnt);
}
printf("%d\n",ans);
return 0;
}
T3:1496. 页
真的没有明白题目名字和题目之间的关联。
题目大意:给出一个数列,每次可以把中间的数放在队头或队尾,求最小步数,无解输出“No Answer"。
直接宽搜,封顶 9 ! 9! 9!。用哈希或者 m a p map map判重。
#include
#include
#include
#include
using namespace std;
const int P = 998244353;
template < class T >
inline void read(T &x)
{
char ch = getchar(); x = 0; int fg = 1;
for(;ch < '0' || ch > '9';) fg = ch == '-' ? -1 : 1, ch = getchar();
for(;ch >= '0' && ch <= '9';) x = x * 10 + (ch ^ '0'), ch = getchar(); x *= fg;
}
map < int,int > dep;
struct node{ int id,v; } b[15];
int n,a[15],q[400010][15],l,r,cur,now,c[15];
int cmp(node a,node b) { return a.v < b.v; }
int main()
{
read(n);
for(int i = 1;i <= n; ++ i)
read(a[i]), b[i] = (node){ i,a[i] }, q[1][i] = i, cur = 1ll * (1ll * cur * 10 % P + i) % P;
sort(b + 1,b + 1 + n,cmp); for(int i = 1;i <= n; ++ i) now = 1ll * (1ll * now * 10 % P + b[i].id) % P;
if(now == cur) { printf("0"); return 0; } l = r = 1, dep[cur] = 1;
for(;l <= r; ++ l)
{
int tmp = 0,stp; cur = 0;
for(int i = 1;i <= n; ++ i) c[i] = q[l][i], cur = 1ll * (1ll * cur * 10 % P + c[i]) % P; stp = dep[cur];
for(int i = 1;i <= n / 2; ++ i) swap(c[i],c[n / 2 + 1]);
for(int i = 1;i <= n; ++ i) tmp = 1ll * (1ll * tmp * 10 % P + c[i]) % P;
if(!dep[tmp])
{
int fg = 1; ++r; for(int i = 1;i <= n; ++ i) { q[r][i] = c[i]; if(c[i] != b[i].id) fg = 0; }
if(fg) { printf("%d\n",stp); return 0; } dep[tmp] = stp + 1;
} tmp = 0;
for(int i = 1;i <= n; ++ i) c[i] = q[l][i];
for(int i = n;i > n / 2 + 1; -- i) swap(c[i],c[n / 2 + 1]);
for(int i = 1;i <= n; ++ i) tmp = 1ll * (1ll * tmp * 10 % P + c[i]) % P;
if(!dep[tmp])
{
int fg = 1; ++r; for(int i = 1;i <= n; ++ i) { q[r][i] = c[i]; if(c[i] != b[i].id) fg = 0; }
if(fg) { printf("%d\n",stp); return 0;} dep[tmp] = stp + 1;
}
}
printf("No Answer\n");
return 0;
}
话说我这个模数好像没什么用欸。。。
T4:1497. 景点中心
题目大意:给出一棵树,每个点有点权,以一个点为根的代价是其它点到这个点的距离乘上它的点权。求最小代价。
这又是一道在模板不过的换根。
#include
#include
using namespace std;
const int N = 100010;
typedef long long ll;
template < class T >
inline void read(T &x)
{
char ch = getchar(); x = 0; int fg = 1;
for(;ch < '0' || ch > '9';) fg = ch == '-' ? -1 : 1, ch = getchar();
for(;ch >= '0' && ch <= '9';) x = x * 10 + (ch ^ '0'), ch = getchar(); x *= fg;
}
struct Edge{ int to,nxt,val; } g[N << 1];
int last[N],cnt = 0,n,id; ll mn,w,f[N],a[N];
void add(int u,int v,ll w) { g[++cnt] = (Edge){ v,last[u],w }, last[u] = cnt; }
void dfs(int x,int fa)
{
f[x] = a[x];
for(int i = last[x];i;i = g[i].nxt)
if(g[i].to != fa) dfs(g[i].to,x), f[x] += f[g[i].to], mn += f[g[i].to] * g[i].val;
}
void dfs(int x,int fa,ll cur)
{
if(cur < mn) { id = x, mn = cur; }
for(int i = last[x];i;i = g[i].nxt)
if(g[i].to != fa) dfs(g[i].to,x,cur + (f[1] - 2 * f[g[i].to]) * g[i].val);
}
int main()
{
read(n), id = 1;
for(int i = 1;i <= n; ++ i) read(a[i]);
for(int i = 1,u,v;i < n; ++ i)
read(u), read(v), read(w), add(u,v,w), add(v,u,w);
dfs(1,0), dfs(1,0,mn), printf("%d\n%lld\n",id,mn);
return 0;
}
这次比赛还是暴露了一些问题,比如说像扫描线这种最基础的算法都没有掌握的十分熟练,还需要考场手推,如果其它题没有像今天这么简单的话很可能就会打乱节奏。所以还是要多多练习。
明天还是回省B。(不过什么时候能出一个介于省B和提高间的组呢?