洛谷NOIp熱身賽題解
洛谷NOIp熱身賽題解
A 最大差值
簡單樹狀陣列,維護區間和、區間平方和,方差按照給的公式算就行了
#include<bits/stdc++.h> #define il inline #define vd void #define mod 1000000007 typedef long long ll; namespace IO{ const int maxn=(1<<21)+1; char ibuf[maxn],*iS,*iT,c;int f; inline char getc(){ return iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,maxn,stdin),iS==iT?EOF:*iS++):*iS++; } inline int gi(){ int x=0; for(f=1,c=getc();(c<'0'||c>'9');c=getc())f=c=='-'?-1:1; for(x=0;(c>='0'&&c<='9');c=getc())x=(x<<1)+(x<<3)+(c^48); return x*f; } } using IO::gi; int b[100010]; int inv[100010]; int sum[100010<<4],SUM[100010<<4],N; #define mid ((l+r)>>1) il vd build(int n){ N=1<<(int)ceil(log(n+2)/log(2)); for(int i=1;i<=n;++i)sum[i+N]=b[i],SUM[i+N]=1ll*b[i]*b[i]%mod; for(int i=N-1;i;--i)sum[i]=(sum[i<<1]+sum[i<<1|1])%mod,SUM[i]=(SUM[i<<1]+SUM[i<<1|1])%mod; } il vd update(int p,int d){ p+=N,sum[p]=d,SUM[p]=1ll*d*d%mod; for(p>>=1;p;p>>=1)sum[p]=(sum[p<<1]+sum[p<<1|1])%mod,SUM[p]=(SUM[p<<1]+SUM[p<<1|1])%mod; } il std::pair<int,int> query(int l,int r){ --l,++r;l+=N,r+=N; int ret=0,RET=0; while(l^r^1){ if(~l&1)ret=(sum[l^1]+ret)%mod,RET=(SUM[l^1]+RET)%mod; if(r&1)ret=(sum[r^1]+ret)%mod,RET=(SUM[r^1]+RET)%mod; l>>=1;r>>=1; } return std::make_pair(ret,RET); } int main(){ int n=gi(),m=gi(); inv[1]=1;for(int i=2;i<=n;++i)inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod; for(int i=1;i<=n;++i)b[i]=gi(); build(n); int o,l,r; while(m--){ o=gi(); if(o==1)l=gi(),r=gi(),update(l,r); else{ l=gi(),r=gi(); std::pair<int,int>A=query(l,r); int sa=A.first,SA=A.second; int pa=1ll*sa*inv[r-l+1]%mod; printf("%lld\n",1ll*inv[r-l+1]*(1ll*pa*pa%mod*(r-l+1)%mod+SA-1ll*2*sa*pa%mod+mod)%mod); } } return 0; }
B 攀爬者
略
C 蜈蚣
簡單dp,設f[i][j]為1-j分成i段的最大收益,f[i][j]=max(f[i-1][k]+(A[k+1] xor ... xor A[j]))
#include<bits/stdc++.h> #define il inline #define vd void typedef long long ll; il int gi(){ int x=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-')f=-1; ch=getchar(); } while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } int f[2][1010],W[1010]; il vd chkmx(int&a,int b){if(b>a)a=b;} int main(){ int n=gi(),m=gi(); register int x=0,i,j,k; for(i=1;i<=n;++i)W[i]=gi()^W[i-1]; memset(f[x]+1,-127,n<<2); for(i=1;i<=m;++i){ x^=1; memset(f[x],-127,i<<2); for(j=i;j<=n;++j){ f[x][j]=0; for(k=i-1;k<=j;++k) chkmx(f[x][j],f[x^1][k-1]+(W[j]^W[k-1])); } } printf("%d\n",f[x][n]); return 0; }
D 漂浮的鴨子
同訊息傳遞,略
E 最大差值
略
F 隨機數生成器
70分可以dp,設\(f[i]\)表示\(i\)到\(1\)的期望,那麼\(f[1]=0,f[i]=1+\frac{\sum_{j=1}^{i}f[j]}{i}(i>1)\)
\(\frac{i-1}{i}f[i]=1+\frac{\sum_{j=1}^{i-1}f[j]}{i}\)
然後100分可以發現\(f[i]=1+(1/1+1/2+...+1/(i-1))\),後面的調和級數可以直接套公式
#include<bits/stdc++.h> #define il inline #define vd void typedef long long ll; il int gi(){ int x=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-')f=-1; ch=getchar(); } while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } int main(){ int n=gi();if(n==1)return puts("0.00000"),0; double ans=1; if(n<=1000000)for(int i=1;i<n;++i)ans+=1.0/i; else ans=1+log(n-1)+0.5772156649015; printf("%.5lf\n",ans); return 0; }
G 大迴圈
題面看著很nb,實際上上下的a沒有關係,F(q)是個定值,答案就是\(C_{n}^{k}\times F(q)\)
#include<bits/stdc++.h>
#define il inline
#define vd void
#define mod 1000000007
#define int ll
typedef long long ll;
il ll gi(){
ll x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
int p[500010];
il int pow(int x,int y){
int ret=1;
while(y){
if(y&1)ret=1ll*ret*x%mod;
x=1ll*x*x%mod;y>>=1;
}
return ret;
}
signed main(){
ll n=gi(),m=gi(),k=gi(),q=gi()%mod,F=0;
for(int x=1,i=0;i<=m;++i,x=1ll*x*q%mod)F=(F+1ll*x*gi()%mod)%mod;
p[0]=1;for(int i=1;i<=n;++i)p[i]=1ll*p[i-1]*i%mod;
printf("%lld\n",1ll*p[n]*pow(1ll*p[k]*p[n-k]%mod,mod-2)%mod*F%mod);
return 0;
}
H 會議座位
略
I 生日禮物
選兩個數使得lcm=a,單獨考慮a的每個質因數\(p^k\),那麼這兩個數一定有一個會有\(p^k\),另一個可以是\(p^{(0-k)}\),所以\(p^k\)有\(2k+1\)中選法。給a分解質因數,\(ans=\Pi(2k+1)\)
J HKE與他的小朋友
略
K 寶藏
二維樹狀陣列 區間異或 區間查詢異或和
和一維樹狀陣列區間修改區間查詢一樣,記\(d(x,y)\)為\((x,y,n,n)\)這個子矩形的共同增量,查詢\((x_0,y_0)\)只需查詢\((1,1,x_0,y_0)\)的異或和。
冷靜分析,\(d(x,y)\)在\((1,1,x_0,y_0)\)出現了奇數次才要計算\(d(x,y)\),條件就是\((x_0-x+1)(y_0-y+1)\)為奇數,那麼\(x,x_0\)同奇偶,\(y,y_0\)同奇偶,對於每一種\(x,y\)的奇偶情況開一個樹狀陣列即可,一共4個
#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
il int gi(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,m,N;
int t[2][2][1271][1271];
il vd update(int x,int y,int d){
int X=x&1,Y=y&1;x=(x+1)>>1,y=(y+1)>>1;
for(int i=x;i<=N;i+=i&-i)
for(int j=y;j<=N;j+=j&-j)
t[X][Y][i][j]^=d;
}
il int query(int x,int y){
int ret=0,X=x&1,Y=y&1;x=(x+1)>>1,y=(y+1)>>1;
for(int i=x;i;i-=i&-i)
for(int j=y;j;j-=j&-j)
ret^=t[X][Y][i][j];
return ret;
}
int main(){
n=gi(),m=gi();N=(n+1)>>1;
int x0,y0,x1,y1;char ch;
for(int i=1;i<=m;++i){
do ch=getchar();while(ch!='P'&&ch!='Q');
x0=gi(),y0=gi(),x1=gi(),y1=gi();
if(ch=='P'){
int t=gi(),S=0,a,b;
while(t--)a=gi(),b=gi(),S^=(b&1)<<(a-1);
update(x0,y0,S);
update(x0,y1+1,S);
update(x1+1,y0,S);
update(x1+1,y1+1,S);
}else{
int ans=query(x1,y1)^query(x1,y0-1)^query(x0-1,y1)^query(x0-1,y0-1);
for(int i=0;i<30;++i)putchar('1'+((ans>>i)&1));
puts("");
}
}
return 0;
}
L 簡單的函式
回顧一下題目意思,對每個\(x\)找到最小的\(t\)滿足\(t\not\mid x\),\(f(x)=f(t)+1\)
倒過來考慮,對每個\(t\)會轉移到多少個\(x\)。(稱為\(t\)的答案)\(x\)如果從\(t\)轉移過來,那麼\(lcm(1,2,...,t-1)\mid x,lcm(1,2,...,t)\not\mid x\)。
先考慮\(lcm(1,2,...,t-1)|x\),也就是說\(x\)是\(lcm(1,2,...,t-1)\)的倍數。滿足這個的\(x\)有\(\lfloor\frac{n}{lcm(1,2,...,t-1)}\rfloor\)個。還要減去\(lcm(1,2,...,t)\mid x\)的情況,也就是所有\(t'>t\)的答案。
注意到\(lcm(1,...,42)\)大約是\(2*10^{17}\),那麼可行的\(t\)不會超過\(42\),從\(42\)開始算即可。
#include<bits/stdc++.h>
#define il inline
#define vd void
#define mod 1000000007
#define int ll
typedef long long ll;
il int gi(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
il ll lcm(ll a,ll b){return a/std::__gcd(a,b)*b;}
ll f[]={0,0,1,2,3,2,4,2,3,2,3,2,3,2,3,2,3,2,4,2,3,2,3,2,3,2,3,2,3,2,4,2,3,2,3,2,3,2,3,2,3,2,4,2};
il int pow(int x,int y){
int ret=1;
while(y){
if(y&1)ret=1ll*ret*x%mod;
x=1ll*x*x%mod;y>>=1;
}
return ret;
}
ll L[50],a[50];
signed main(){
int n=gi();
L[1]=1;for(int i=2;i<=42;++i)L[i]=lcm(i,L[i-1]);
ll ans=1,sum=0;
for(int i=42;i;--i){
a[i]=n/L[i]-sum;
if(L[i]<=2)--a[i];
if(i==1)--a[i];
ans=1ll*ans*pow(f[i+1]+1,a[i])%mod;
sum+=a[i];
}
printf("%lld\n",ans);
return 0;
}
M 數列遊戲
先dp一遍,\(f[l][r]\)表示\([l,r]\)這個區間裡的數能否被消完,那麼\(f[i][i+1]=[gcd(A[i],A[i+1])!=1]\)
\(f[l][r]\)有兩種轉移
- \(f[l+1][r-1] (gcd(A[l],A[r])!=1)\),先消完\([l+1,r-1]\),再消掉\(l,r\)
- \(f[l][k],f[k+1][r]\),消掉\([l,k]\)到\([k+1],r\)
這一段是\(O(n^3)\)的(我也不知道為啥能過)
然後就知道了哪些區間能用。剩下的看程式碼吧
#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
il int gi(){
int x=0;
char ch=getchar();
while(!isdigit(ch)){
ch=getchar();
}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x;
}
ll A[1001],B[1001];
bool f[810][810];
ll g[810],sg[810];
il void chkmax(ll &a,ll b){if (a<b)a=b;}
int main(){
register int n=gi(),i,l,r,s;
for(i=1;i<=n;++i)A[i]=gi();
for(i=1;i<=n;++i)B[i]=gi()+B[i-1];
for(i=1;i<n;++i)if(std::__gcd(A[i],A[i+1])!=1)f[i][i+1]=1;
for(s=4;s<=n;s+=2)
for(l=1;l+s-1<=n;++l){
r=l+s-1;
if(std::__gcd(A[l],A[r])!=1&&f[l+1][r-1])f[l][r]=1;
for(i=l+1;i<=r&&!f[l][r];i+=2)
f[l][r]|=f[l][i]&f[i+1][r];
}
for(r=1;r<=n;++r){//依次加入右端點為r的區間
// g[r]表示區間[1,r]的最大收益,sg[r]=max(g[1],...,g[r])
for(l=1;l<=r;++l)if(f[l][r])chkmax(g[r],B[r]-B[l-1]+sg[l-1]);
for(i=1;i<=r;++i)sg[i]=std::max(sg[i-1],g[i]);
}
printf("%lld\n",sg[n]);
return 0;
}