2020.08.13【NOIP提高组】模拟 总结

2020.08.13【NOIP提高组】模拟 总结

再次AK了呢。(好像某谷上说宜做模拟赛我就能AK?
不过确实比昨天简单了不少。

一下又是十分简略的题解。。。

T1:3566. 阶乘
题目大意:求n的阶乘在base进制下末尾零的个数。(n和base均为10进制数

显然对于base进制,当一个数是base的倍数时末尾会有零。所以转化题意得:求m,使得 b a s e m ∗ k = n ! base^{m}*k=n! basemk=n!。然后将base分解质因数,然后在 n ! n! n!里面配对就好了。

考场直接上了Pollard_rho,纯粹为了好玩
话说base可以开到1e18(逃,还是不要互相残害吧

#include <ctime> 
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 10010;

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;
}

ll T,n,base,cur,tot,p[N],cnt[N],ans;

ll gcd(ll a,ll b) { return !b ? a : gcd(b,a % b); }

ll f_mul(ll a,ll b,ll P) { return (a * b - (ll)((long double)a / P * b) * P + P) % P; }

ll f_pow(ll a,ll x,ll P) { ll ret = 1; a = a % P; for(;x;x >>= 1) (x & 1) && (ret = f_mul(ret,a,P),1), a = f_mul(a,a,P); return ret % P; }

bool witness(ll x,ll p)
{
	if(f_pow(x,p - 1,p) != 1) return 0;
	for(ll k = p - 1,t;(k & 1) == 0;)
	{
		k >>= 1, t = f_pow(x,k,p);
		if((t ^ 1) && (t ^ p - 1)) return 0;
		if(t == p - 1) return 1;	
	}
	return 1;
}

bool Miller_rabin(ll n)
{
	if(n == 1) return 0;
	const int c[] = { 2,3,7,61,24251,19260817 };
	for(int i = 0;i < 6; ++ i)
		if(n == c[i]) return 1; else if(!witness(c[i],n)) return 0;
	return 1;
}

ll r,q[N];

void Pollard_rho(ll n)
{
	for(;!(n & 1);) n >>= 1, q[++r] = 2;
	if(n == 1) return;
	if(Miller_rabin(n)) { q[++r] = n; return; }
	ll x = rand() % (n - 1) + 1,y = x,c = rand() % (n - 1) + 1,g;
	for(ll i = 1,j = 1; ; ++ i)
	{
		x = (f_mul(x,x,n) + c) % n, g = gcd(abs(x - y),n);
		if(x == y) x = rand() % (n - 1) + 1,y = x,c = rand() % (n - 1) + 1,i = 0,j = 1;
		if((g > 1) && (g < n)) break;
		if(i == j) y = x,j <<= 1;
	}
	Pollard_rho(g), Pollard_rho(n / g);
}

int main()
{
	for(read(T);T--;cur = 0)
	{
		read(n), read(base), r = tot = ans = 0, Pollard_rho(base);
		sort(q + 1,q + 1 + r); for(int i = 1;i <= r; ++ i)
			if(q[i] != cur) p[++tot] = q[i], cnt[tot] = 1, cur = q[i];
			else ++cnt[tot];
		for(int i = 1;i <= tot; ++ i)
		{
			ll tmp = 0;
			for(ll x = n;x;x /= p[i]) tmp += x / p[i];
			if(!ans) ans = tmp / cnt[i]; else ans = min(ans,tmp / cnt[i]);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

T2:3567. 石油储备计划
题目大意:给出一棵树,树上的点有石油,点之间的双向边有边权,一个单位的石油通过一条边的花费是这条边的距离。求在每个点石油尽量平均的情况下的最小花费。

昨天刚学完上下界网络流。。。最近摸清了XC的出题特点,就是今天的新知识点明天或后天一定会考。

如果会上下界网络流的话这就是一道版子题了。

#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 10010,INF = 0x3f3f3f3f;

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,flow,cost; } g[(N * 100) << 1];
int last[N],cnt = 1,a[N],cur[N];
int Q,vis[N],dis[N],n,S,T,ans,s,t,sum;
long long cos;

void add(int u,int v,int w1,int w2) { g[++cnt] = (Edge){ v,last[u],w1,w2 }, last[u] = cnt; }

void Add_Edge(int u,int v,int w1,int w2) { add(u,v,w1,w2), add(v,u,0,-w2); }

int spfa(int s,int t)
{
	memset(vis,0,sizeof vis), memset(dis,0x3f,sizeof dis);
	dis[t] = 0, vis[t] = 1; deque < int > q; q.push_back(t);
	while(!q.empty())
	{
		int x = q.front(); q.pop_front();
		for(int i = last[x];i;i = g[i].nxt)
			if(g[i ^ 1].flow && dis[g[i].to] > dis[x] - g[i].cost)
			{
				dis[g[i].to] = dis[x] - g[i].cost;
				if(!vis[g[i].to])
				{
					vis[g[i].to] = 1;
					if(!q.empty() && dis[g[i].to] < dis[q.front()]) 
						q.push_front(g[i].to); else q.push_back(g[i].to);
				}
			}
		vis[x] = 0;
	} return dis[s] < INF;
}

int dfs(int x,int flow)
{
	if(x == t) { vis[t] = 1; return flow; }
	int now = 0; vis[x] = 1;
	for(int i = last[x];i;i = g[i].nxt)
		if(!vis[g[i].to] && g[i].flow && dis[g[i].to] == dis[x] - g[i].cost)
		{
			int tmp = dfs(g[i].to,min(flow,g[i].flow));
			g[i].flow -= tmp, g[i ^ 1].flow += tmp;
			cos += 1ll * tmp * g[i].cost, now += tmp;
			if(now == flow) return now;
		}
	return now;
}

int main()
{
	for(read(Q);Q--;)
	{
		memset(last,0,sizeof last), cnt = 1, sum = 0, cos = 0, memset(cur,0,sizeof cur);
		read(n); S = n + 1, T = S + 1, s = n + 3, t = s + 1, Add_Edge(T,S,INF,0);
		for(int i = 1;i <= n; ++ i)
			read(a[i]), cur[S] -= a[i], cur[i] += a[i], sum += a[i], Add_Edge(S,i,0,0);
		for(int i = 1;i <= n; ++ i)
			cur[T] += (sum / n), cur[i] -= (sum / n), Add_Edge(i,T,1,0);
		for(int i = 1,u,v,w1;i < n; ++ i)
			read(u), read(v), read(w1), Add_Edge(u,v,INF,w1), Add_Edge(v,u,INF,w1);
		for(int i = 1;i <= T; ++ i) if(cur[i] > 0) Add_Edge(s,i,cur[i],0); else Add_Edge(i,t,-cur[i],0);
			while(spfa(s,t))
				do { memset(vis,0,sizeof vis), ans += dfs(s,INF); } while(vis[t]);
		printf("%lld\n",cos);
	}
	return 0;
}

T3:3570. 壕壕的寒假作业

这题就是白送分给你,DAG上正着反着跑一遍就没了。
代码不贴了。。。

T4:3571. 内存分配

没有题目大意。我语文不好不知道该怎么表达。。。(懒

首先容易发现,按b从小到大的顺序一定不会有错。

假设有两个程序x和y,默认 x b < y b x_{b}<y_{b} xb<yb,完成这两个程序需要的最少内存是 m a x ( x b , y b − x a ) max(x_{b},y_{b}-x_{a}) max(xb,ybxa)。然后这个过程用线段树来完成就可以了。

这道题的话把它离线做,然后每次询问把程序的前一个版本删掉,插入新的版本就行了。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;

const ll N = 200010;

template < class T >
inline void read(T &x)
{
	char ch = getchar(); x = 0; ll 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
{ 
	ll a,b,id; 
	node operator + (const node &x) const
	{
		node sum = (node){ 0,0,0 };
		sum.b = max(b,x.b - a), sum.a = a + x.a;
		return sum;
	}
} p[N],t[N << 2];
ll n,m,id[N],rk[N],cur[N];

ll cmp(node a,node b) { return a.b < b.b || (a.a == b.a && a.a > b.a); }

#define ls p << 1
#define rs ls | 1

node merge(node a,node b)
{
	node sum = (node){ 0,0,0 };
	sum.b = max(a.b,b.b - a.a), sum.a = a.a + b.a;
	return sum;
}

void change(ll p,ll x,node k,ll tl,ll tr)
{
	if(tl == tr) { t[p] = (node){ k.a,k.b,k.id }; return; }
	ll mid = tl + tr >> 1;
	x <= mid ? change(ls,x,k,tl,mid) : change(rs,x,k,mid + 1,tr);
	t[p] = t[ls] + t[rs];
}

int main()
{
	read(n), read(m);
	for(ll i = 1,a,b;i <= n; ++ i)
		read(a), read(b), p[i] = (node){ a,b,i };
	for(ll i = 1,a,b;i <= m; ++ i)
		read(id[i]), read(a), read(b), p[i + n] = (node){ a,b,i + n };
	sort(p + 1,p + 1 + n + m,cmp);
	for(ll i = 1;i <= n + m; ++ i) rk[p[i].id] = i;
	for(ll i = 1;i <= n; ++ i) change(1,rk[i],p[rk[i]],1,n + m), cur[i] = rk[i];
	for(ll i = 1;i <= m; ++ i)
		change(1,cur[id[i]],(node){ 0,0,0 },1,n + m), change(1,rk[i + n],p[rk[i + n]],1,n + m),
		cur[id[i]] = rk[i + n], printf("%lld\n",t[1].b);
	return 0;
}

今天又是奇葩的2hA3题,剩下死肝T4QWQ。我太菜了。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值