1. 程式人生 > >【NOIP2018模擬賽2018.10.25 B組】

【NOIP2018模擬賽2018.10.25 B組】

此題爆零。原因不知道可以因費馬小定理推出 b' = b MOD p。

可以看出,f(u,p) = v 在這道題裡面意思就是 v = u MOD p,由於p是一個質數,那麼可以知道所有的a[i]等價於a[i] % p,所有的b[i]等價於b[i] % (p-1)

懶得講部分分 正解如下:

1、首先我們可以看出處理過後所有的a,b滿足 0 <= a <= p-1,0 <= b <= p-2,又 p <= 5000 ,所以我們只需要算a,b的同時開倆桶記錄0~p-1(p-2)分別在算a和算b中出現的次數即可,不需要實實在在列舉a,b。

2、算出次數後,也不需要用快速冪算次方了,可以看出 b 最多才5000-2,我們列舉a定下來,算次方時一個變數累計就好了。

這樣演算法為O(n+m+p^2),可以過。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt putchar
#define gc getchar
#define ex pt('\n')
#define ko pt(' ')
const int MAXN = 1e6 + 5;
const int P = 5e3 + 5;
ll p,q;
ll n,a[MAXN],A,B,C;
ll m,b[MAXN],D,E,F;
ll cnta[MAXN],cntb[MAXN],now;
ll sum = 0;
void in(ll &x)
{
	ll num = 0,f = 1; char ch = gc();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void out(ll x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x % 10 + '0');
}

ll quick_mi(ll a,ll b)
{
	a %= p;
	ll ans = 1;
	while(b)
	{
		if(b & 1) 
			ans = a*ans % p;
		a = a*a % p;
		b >>= 1;
	}
	return ans % p;
}

inline void init()
{
	in(p); in(q);
	in(n); in(a[1]); in(a[2]); in(A); in(B); in(C);
	in(m); in(b[1]); in(b[2]); in(D); in(E); in(F);
	a[1] %= p,a[2] %= p;
	cnta[a[1]]++; cnta[a[2]]++;
	b[1] %= (p-1),b[2] %= (p-1);
	cntb[b[1]]++; cntb[b[2]]++;
	for(int i = 3;i <= n;i++)
	{
		a[i] = ((A*a[i-1] % p - (B*a[i-2] % p) + p) % p - C + p) % p;
		cnta[a[i]]++;
	}
	for(int i = 3;i <= m;i++)
	{
		b[i] = (((D*b[i-1] % (p-1) + E*b[i-2] % (p-1)) % (p-1) + (p-1)) % (p-1) + F + (p-1)) % (p-1);
		cntb[b[i]]++;
	}
}
inline void work()
{
	for(int i = 0;i <= p-1;i++)
	{
		if(!cnta[i]) continue;
		now = 1;
		for(int j = 0;j <= p-2;j++)
		{
			if(now <= q) sum += cntb[j]*cnta[i];
			now = now * i % p;
		}
	}
	out(sum);
}

int main()
{
	init();
	work();
	return 0;
}

同樣懶得說部分分你如果不想打正解就直接全排列40分+特判10分 = 50分不動腦子就可以得到。

正解考慮貪心。

先sort一遍,然後建立一個空序列,從小到大加入人,儘量往左邊加,用線段樹維護其正確性,最後序列即為答案。

可以看出一個數要想合法地加入序列中,那麼一定要保證其向左或是向右(取其小值)可供比此數大的數加入的空格,要等於資料中所給出的此人記下來的比他高的人數,這樣就一定能滿足正確性。

你可以n^2 列舉數 + 插入數 70分。

當然線段樹相當於把插入過程變為logn的,也很簡單,就一開始整個線段樹中空格數鋪滿,後面加人儘量向左加,加的位置空格數-1,然後updata一下上面的節點資訊就可以啦。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt putchar
#define gc getchar
#define ex pt('\n')
#define ko pt(' ')
#define lx x << 1
#define rx x << 1 | 1
const int MAXN = 1e5 + 5;
int n,space[MAXN<<2];
int ans[MAXN];
struct person
{
	int h,idx,higher;
	bool operator < (const person one) const
	{
		return h < one.h;
	}
}a[MAXN];
void in(int &x)
{
	int num = 0,f = 1; char ch = gc();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void out(int x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x % 10 + '0');
}

void build(int x,int l,int r)
{
	if(l == r) {space[x] = 1; return;}
	int mid = (l + r) >> 1;
	build(lx,l,mid);
	build(rx,mid+1,r);
	space[x] = space[lx] + space[rx];
}

void updata(int x,int l,int r,int pos)
{
	if(l == r) {space[x]--; return;}
	int mid = (l + r) >> 1;
	if(pos <= mid) updata(lx,l,mid,pos);
	else updata(rx,mid+1,r,pos);
	space[x] = space[lx] + space[rx];
}

int ask(int x,int l,int r,int need)
{
	if(l == r) return l;
	int mid = (l + r) >> 1;
	if(need <= space[lx]) ask(lx,l,mid,need);
	else ask(rx,mid+1,r,need - space[lx]);
}

int main()
{
	in(n);
	for(int i = 1;i <= n;i++) in(a[i].h),in(a[i].higher);
	sort(a+1,a+1+n);
	build(1,1,n);
	for(int i = 1;i <= n;i++)
	{
		int num = min(a[i].higher,n - i - a[i].higher) + 1;
		if(num <= 0 || num > n) {cout << "impossible"; return 0;}
		int now = ask(1,1,n,num);
		ans[now] = a[i].h;
		updata(1,1,n,now);
	}
	for(int i = 1;i < n;i++) out(ans[i]),ko; out(ans[n]);
	return 0;
}

第三題:不會,看不懂,學不來。