【NOIP2018模擬賽2018.10.25 B組】
阿新 • • 發佈:2018-11-06
此題爆零。原因不知道可以因費馬小定理推出 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;
}
第三題:不會,看不懂,學不來。