NOIP2011提高組解析
題目描述:
day1:
鋪地毯:
只有一個需要注意的地方:給出的g和k不是右下角的座標,右下角座標應是(a+g,b+k)
倒序判斷即可。
參考程式:
#include<cstdio> #include<algorithm> #define maxn 1100000 using namespace std; int x1[maxn],x2[maxn]; int y1[maxn],y2[maxn]; int n; int main(){ freopen("carpet.in","r",stdin); freopen("carpet.out","w",stdout); scanf("%d",&n); for (int i=0;i<n;i++) scanf("%d%d%d%d",&x1[i],&y1[i],&x2[i],&y2[i]); int x0,y0; scanf("%d%d",&x0,&y0); for (int i=n-1;i>=0;i--) if (x1[i]<=x0 && x0<=x1[i]+x2[i] && y1[i]<=y0 && y0<=y1[i]+y2[i]){ printf("%d",i+1); return 0; } printf("-1"); return 0; }
選擇客棧:
看似需要兩層迴圈,實際上細細分析只是簡單的推理模擬。
讀入col和p
令a[i]=top[col]表示當前顏色的前一次出現實在幾號。
b[i]表示前一個價格小於標準的是在幾號。
cnt[i]=top2[col]++表示當前顏色是第幾次出現。
第二次掃瞄:
若a[i]<=b[i]即當前與之前同顏色的之間必然有一個客棧價格小於標準,ans[i]+=cnt[i]
反之,當前客棧與之前同顏色的之間是否有客棧價格小於標準,只需看ans[a[i]],
所以此時ans[i]=ans[a[i]];
最後將ans求和即可。
參考程式:
#include<cstdio> #include<algorithm> #define maxn 211000 using namespace std; int cnt[maxn],top[maxn]; int top2[maxn],a[maxn],b[maxn],c[maxn]; int n,k,cost; int main(){ freopen("hotel.in","r",stdin); freopen("hotel.out","w",stdout); scanf("%d%d%d",&n,&k,&cost); for (int i=0;i<n;i++){ int col,p; scanf("%d%d",&col,&p); a[i]=top[col];top[col]=i; if (p<=cost)b[i]=i;else b[i]=b[i-1]; cnt[i]=top2[col]++; } for (int i=0;i<n;i++) if (a[i]<=b[i])c[i]=cnt[i]; else c[i]=c[a[i]]; int ans=0; for (int i=0;i<n;i++) ans+=c[i]; printf("%d",ans); return 0; }
Mayan遊戲:
此問題裸裸搜尋即可,
只需注意幾個剪枝:
1.左右相同則不搜
2.只考慮右,除非當前點的左邊是0才考慮左。
然後就可以過了。
參考程式:
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n; int a[10][10],b[10][10],c[10][10]; bool d[10][10]; int x[10],y[10],t[10]; bool ok(){ for (int i=1;i<=5;i++) if (b[1][i])return false; return true; } void print(){ for (int k=1;k<=n;k++) printf("%d %d %d\n",y[k]-1,x[k]-1,t[k]); exit(0); } void dfs(int step); void remain(int step){ bool still=true; /*printf("%d,\n",step); for (int i=1;i<=7;i++){ for (int j=1;j<=5;j++) printf("%d ",b[i][j]); printf("\n"); }*/ while (still){ still=false; memset(d,0,sizeof(d)); for (int i=1;i<=7;i++) for (int j=1;j<=5;j++) if (b[i][j]){ if (j+2<=5 && b[i][j]==b[i][j+1] && b[i][j]==b[i][j+2]){ d[i][j]=d[i][j+1]=d[i][j+2]=true; } if (i+2<=7 && b[i][j]==b[i+1][j] && b[i][j]==b[i+2][j]){ d[i][j]=d[i+1][j]=d[i+2][j]=true; } } for (int i=1;i<=7;i++) for (int j=1;j<=5;j++) if (d[i][j])b[i][j]=0; for (int j=1;j<=5;j++) for (int i=1;i<=7;i++) if (!b[i][j]){ int tt=i; while ((b[tt][j]==0) && tt<=7)tt++; if (tt>7)break; for (int k=0;k<=tt-i;k++){ b[i+k][j]=b[tt+k][j]; b[tt+k][j]=0; } still=true; } if (!still)break; } /*printf("%d,\n",step); for (int i=1;i<=7;i++){ for (int j=1;j<=5;j++) printf("%d ",b[i][j]); printf("\n"); }*/ if (step==n+1 && ok())print(); if (step!=n+1 && ok())return; dfs(step); } void dfs(int step){ if (step>n)return; int c[10][10]; memcpy(c,b,sizeof(c)); //printf("%d\n",step); for (int j=1;j<=5;j++){ for (int i=1;i<=7;i++) if (b[i][j]&& b[i][j] != b[i][j+1]){ //if (step==1)printf("pay attention to (%d,%d)\n",i,j); //if (x[1]==2 && y[1]==3 && x[2]==2 && y[2]==4)printf("pay attention to (%d,%d)!\n",i,j); if (j<5 ){ x[step]=i;y[step]=j;t[step]=1; swap(b[i][j],b[i][j+1]); remain(step+1); memcpy(b,c,sizeof(b)); } if (j==1)continue; if (b[i][j-1])continue; if (b[i][j-1]==b[i][j])continue; x[step]=i;y[step]=j;t[step]=-1; swap(b[i][j],b[i][j-1]); remain(step+1); memcpy(b,c,sizeof(b)); } } } int main(){ freopen("mayan.in","r",stdin); freopen("mayan.out","w",stdout); scanf("%d",&n); for (int i=1;i<=5;i++){ int x,j=0; while (scanf("%d",&x)==1 && x)a[++j][i]=x; } for (int i=1;i<=7;i++){ for (int j=1;j<=5;j++) b[i][j]=a[i][j];//printf("%d ",a[i][j]); //printf("\n"); } dfs(1); //remain(1); printf("-1"); return 0; }
day2:
計算係數:
將ax,by看成整體(其實就是裸裸的二項式)
參考程式:
#include<cstdio>
#include<algorithm>
#define Mod 10007
using namespace std;
int f[Mod];
int main(){
freopen("factor.in","r",stdin);
freopen("factor.out","w",stdout);
int a,b,k,n,m;
scanf("%d%d%d%d%d",&a,&b,&k,&n,&m);
a%=Mod;b%=Mod;
int x=1;
for (int i=0;i<n;i++){
x*=a;x%=Mod;
}
int y=1;
for (int i=0;i<m;i++){
y*=b;y%=Mod;
}
f[1]=1;
for (int i=2;i<=k+1;i++)
for (int j=i;j>=2;j--){
f[j]+=f[j-1];
f[j]%=Mod;
}
printf("%d",x*y%Mod*f[m+1]%Mod);
return 0;
}
聰明的質檢員:
根據題目明顯可以知道,W越大,Y越小,所以二分W,使Y逐漸靠近S即可。
在計算Y的時候用到了小小的字首和預處理思想,也就沒什麼了。
參考程式:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#define maxn 210000
using namespace std;
long long s1[maxn],s2[maxn];
int l[maxn],r[maxn];
int w[maxn],v[maxn];
int n,m;
long long S;
long long check(int x){
memset(s1,0,sizeof(s1));
memset(s2,0,sizeof(s2));
for (int i=1;i<=n;i++){
s1[i]=s1[i-1];s2[i]=s2[i-1];
if (w[i]>=x){
s1[i]+=1;
s2[i]+=v[i];
}
}
long long res=0;
for (int i=0;i<m;i++)
res+=(s1[r[i]]-s1[l[i]-1])*(s2[r[i]]-s2[l[i]-1]);
return res;
}
int main(){
freopen("qc.in","r",stdin);
freopen("qc.out","w",stdout);
cin>>n>>m>>S;
for (int i=1;i<=n;i++)
scanf("%d%d",&w[i],&v[i]);
for (int i=0;i<m;i++)
scanf("%d%d",&l[i],&r[i]);
int left=0,right=*max_element(w+1,w+n+1);
while (left+1<right){
int mid=(left+right)/2;
if (check(mid)<=S)right=mid;
else left=mid;
}
cout<<min(abs(check(left)-S),abs(S-check(right)));
return 0;
}
觀光公交:
根據題目可以知道每一個加速器的選擇是獨立的,都滿足選擇時間最多的一段路,所以將貪心重複幾次即可。
貪心策略:
首先計算出車到各個站的時間get[i],以及離開各個站的時間last[i](最後一個到的乘客的時間),因為如果get[i]<last[i],即車到這裡要等乘客,所以在此之前不論用多少的加速器,到這裡就會失去效果,所以據此計算出從i站到g[i]站後就會失去效果,用sum表示到某個站的乘客所等的總時間,取sum[g[i]]-sum[i]最大的第i段路用一個加速器,重新計算get與g。
參考程式:
#include<cstdio>
#include<algorithm>
#define maxn 110000
using namespace std;
int last[maxn],get[maxn];
int d[maxn],sum[maxn];
int b[maxn],t[maxn],a[maxn];
int n,m,k;
int g[maxn];
int ans=0;
int main(){
freopen("bus.in","r",stdin);
freopen("bus.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<n;i++)
scanf("%d",&d[i]);
for (int i=0;i<m;i++){
scanf("%d%d%d",&t[i],&a[i],&b[i]);
sum[b[i]]++;
last[a[i]]=max(last[a[i]],t[i]);
}
for (int i=1;i<=n;i++)
get[i]=max(get[i-1],last[i-1])+d[i-1];
g[n-1]=g[n]=n;
for (int i=n-2;i>0;i--)
if (get[i+1]<=last[i+1])g[i]=i+1;
else g[i]=g[i+1];
for (int i=1;i<=n;i++)
sum[i]+=sum[i-1];
for (int i=0;i<m;i++)
ans+=get[b[i]]-t[i];
while (k--){
int reduce=0;
int l,r;
for (int i=1;i<=n;i++)
if (sum[g[i]]-sum[i]>reduce && d[i]>0){
reduce=sum[g[i]]-sum[i];
l=i;r=g[i];
}
if (r>=n)r=n-1;
d[l]--;
ans-=reduce;
for (int i=l;i<=r;i++)
get[i]=max(get[i-1],last[i-1])+d[i-1];
for (int i=r;i>=l;i--)
if (get[i+1]<=last[i+1])g[i]=i+1;
else g[i]=g[i+1];
}
printf("%d",ans);
return 0;
}