2020.08.11【NOIP提高组】模拟 划水+总结

2020.08.11【NOIP提高组】模拟 划水+总结

感觉这就是普及组的难度吧。仿佛看到了CSP-J2019自己的样子,一个小时切三题,然后剩下的时间死肝一题(不过这次好像一个半小时后肝出来了?

以下是十分简略的题解。。。

T1:1494. 密码
题目大意就是把n个不超过 1 0 24 10^{24} 1024的数乘起来。没必要压位。

#include <cstdio>
#include <cstring>
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 <cstdio>
#include <cstring>
#include <algorithm>
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 <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
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 <cstdio>
#include <cstring>
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和提高间的组呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值