鋪地毯
題目描述
為了準備一個獨特的頒獎典禮,組織者在會場的一片矩形區域(可看做是平面直角坐標系的第一象限)鋪上一些矩形地毯。一共有 n 張地毯,編號從 1 到n 。現在將這些地毯按照編號從小到大的順序平行于坐標軸先后鋪設,后鋪的地毯覆蓋在前面已經鋪好的地毯之上。
地毯鋪設完成后,組織者想知道覆蓋地面某個點的最上面的那張地毯的編號。注意:在矩形地毯邊界和四個頂點上的點也算被地毯覆蓋。
輸入輸出格式
輸入格式:
輸入文件名為carpet.in
。
輸入共n+2 行。
第一行,一個整數n ,表示總共有 n 張地毯。
接下來的n 行中,第 i+1 行表示編號i 的地毯的信息,包含四個正整數 a ,b ,g ,k ,每兩個整數之間用一個空格隔開,分別表示鋪設地毯的左下角的坐標(a ,b )以及地毯在x軸和y 軸方向的長度。
第n+2 行包含兩個正整數 x 和y,表示所求的地面的點的坐標(x ,y)。
輸出格式:
輸出文件名為carpet.out
。
輸出共1 行,一個整數,表示所求的地毯的編號;若此處沒有被地毯覆蓋則輸出-1 。
輸入輸出樣例
輸入樣例#1:
3
1 0 2 3
0 2 3 3
2 1 3 3
2 2
輸出樣例#1:
3
輸入樣例#2:
3
1 0 2 3
0 2 3 3
2 1 3 3
4 5
輸出樣例#2:
-1
說明
【樣例解釋1】
如下圖,1 號地毯用實線表示,2 號地毯用虛線表示,3 號用雙實線表示,覆蓋點(2,2)的最上面一張地毯是 3 號地毯。
【數據范圍】
對于30% 的數據,有 n ≤2 ;
對于50% 的數據,0 ≤a, b, g, k≤100;
對于100%的數據,有 0 ≤n ≤10,000 ,0≤a, b, g, k ≤100,000。
思路
先把所有的地毯離線,然后讀入所求位置之后將地毯倒著來,當前地毯覆蓋目標點,輸出即可
代碼
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int n,a[11000],b[11000],g[11000],k[11000],x,y;
int main(){
scanf("%d",&n);
for (int i = 1;i <= n;i++){
scanf("%d%d%d%d",&a[i],&b[i],&g[i],&k[i]);
}
scanf("%d%d",&x,&y);
for (int i = n;i >= 1;i--){
int you = a[i] + g[i];
int shang = b[i] + k[i];
if (x >= a[i] && y >= b[i] && x <= you && y <= shang)
{
printf("%d\n",i);
return 0;
}
}
printf("-1\n");
return 0;
}
思路
首先弄懂樣例,然后從簡單數據入手找規律。
(ax+by)2=(a*x)2+2abxy+(b*y)^2
(ax+by)3=(a*x)3+3(a^2)b(x^2)y+3a(b2)*x*(y2)+(b*y)^3
(ax+by)4=(a*x)4+4(a^3)b(x^3)y+6(a^2)(b2)*(x2)(y^2)+4a(b^3)x(y^3)+(by)^3
(ax+by)^5=......
通過這幾個簡單的公式可以得出(xn)*(ym)的系數為t(a^n)(b^m),t值如下所示:
1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
.........
其實就是楊輝三角(當然也是所有的組合情況C(k,n))
若f[i,j]表示(a*x+b*y)^i
展開后的系數
(a^n)*(b^m)
的系數為f[i,i-n+1]
又
f[i,j]:=f[i-1,j-1]+f[i-1,j];
結果: ans=f[k,k-n+1](a^n)(b^m)
由于題目要求輸出對10007 取模后的結果,則有:
f[i,j]:=((f[i-1,j-1] mod 10007)+(f[i-1,j]mod 10007))mod 10007;
a^n = ((a^(N-1))mod 10007a)mod 10007
b^m = ((b^(m-1))mod 10007b)mod 10007
(a^n可以邊乘邊取余數的方法做,也可用快速冪)。
注意:邊界條件k=0,k=n等。
代碼
#include<iostream>
#include<cstdio>
#define MAXN 1000+10
#define LL long long
using namespace std;
LL aa[MAXN][MAXN];
LL a,b,k,n,m;
LL ans;
void ready()
{
for(int i=1;i<=k+1;i++)
{
for(int j=1;j<=i;j++)
{
if(j==1||i==j)
{
aa[i][j]=1;
continue;
}
aa[i][j]=(aa[i-1][j]+aa[i-1][j-1])%10007;
}
}
}
void readdata()
{
scanf("%lld%lld%lld%lld%lld\n",&a,&b,&k,&n,&m);
}
int main()
{
readdata();
ready();
int cja=1;
for(int i=1;i<=n;i++)
cja=(cja%10007)*(a%10007)%10007;
int cjb=1;
for(int i=1;i<=m;i++)
cjb=(cjb%10007)*(b%10007)%10007;
ans=(cjb*cja)%10007;
ans=(ans*aa[k+1][n+1])%10007;
printf("%d\n",ans);
}
選擇客棧
題目描述
麗江河邊有n 家很有特色的客棧,客棧按照其位置順序從 1 到n 編號。每家客棧都按照某一種色調進行裝飾(總共 k 種,用整數 0 ~ k-1 表示),且每家客棧都設有一家咖啡店,每家咖啡店均有各自的最低消費。
兩位游客一起去麗江旅游,他們喜歡相同的色調,又想嘗試兩個不同的客棧,因此決定分別住在色調相同的兩家客棧中。晚上,他們打算選擇一家咖啡店喝咖啡,要求咖啡店位于兩人住的兩家客棧之間(包括他們住的客棧),且咖啡店的最低消費不超過 p 。
他們想知道總共有多少種選擇住宿的方案,保證晚上可以找到一家最低消費不超過 p元的咖啡店小聚。
輸入輸出格式
輸入格式:
輸入文件hotel.in
,共n+1 行。
第一行三個整數n ,k ,p,每兩個整數之間用一個空格隔開,分別表示客棧的個數,色調的數目和能接受的最低消費的最高值;
接下來的n 行,第 i+1 行兩個整數,之間用一個空格隔開,分別表示 i 號客棧的裝飾色調和i 號客棧的咖啡店的最低消費。
輸出格式:
輸出文件名為hotel.out
。
輸出只有一行,一個整數,表示可選的住宿方案的總數。
輸入輸出樣例
輸入樣例#1:
5 2 3
0 5
1 3
0 2
1 4
1 5
輸出樣例#1:
3
說明
【輸入輸出樣例說明】
- 2 人要住同樣色調的客棧,所有可選的住宿方案包括:住客棧①③,②④,②⑤,④⑤
- 但是若選擇住4 、5 號客棧的話,4 、5 號客棧之間的咖啡店的最低消費是4 ,而兩人能承受的最低消費是3 元,所以不滿足要求。因此只有前 3 種方案可選。
【數據范圍】
對于30% 的數據,有 n ≤100;
對于50% 的數據,有 n ≤1,000;
對于100%的數據,有 2 ≤n ≤200,000,0<k ≤50,0≤p ≤100 , 0 ≤最低消費≤100。
思路
首先我們看,
- 當找到一個旅店在右邊,若是其左邊有一個符合要求的咖啡店,那么再往左邊看,
- 如果有一個顏色相同的旅店,那么就算是一種住宿方法了,那么如果以這個右邊的旅店作為對應點,將所有在左邊而且顏色與之相同的旅店數相加,就能得出很多種住宿方法了。
- 那么用這個辦法,用所有的對應點對應過去,就能最快的時間內找出所用的酒店了。a數組是記錄同一種顏色中的酒店所出現的最后一次的位置;b數組記錄同一種顏色的酒店的出現次數,而c數組則是臨時記錄當前同樣顏色的酒店出現的次數,也就是為找對應點而進行的臨時記錄
代碼
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,k,p,m,ans;
int a[200010],b[200010],c[200010];
int main()
{
scanf("%d%d%d",&n,&k,&p);
for (int i = 1; i <= n; i++)
{
int k, q;
scanf("%d%d",&k,&q);
if (q <= p) m = i; //如果咖啡店的最低消費地于標準,那么記錄其位置
if (m >= a[k]) c[k] = b[k]; //如果在當前顏色的酒店之前有出現過同樣顏色的酒店那么記錄當前同種顏色的酒店的出現次數
a[k] = i; //記錄同樣顏色的酒店最后一次的出現位置
ans+=c[k]; //每一個酒店都可以作為對應點,所以不需要再去加上任何的判斷,記錄住宿的方法
b[k]++; //記錄出現次數的總數
}
printf("%d",ans);
return 0;
}
聰明的質檢員
題目描述 Description
小 T 是一名質量監督員,最近負責檢驗一批礦產的質量。這批礦產共有n 個礦石,從1到n 逐一編號,每個礦石都有自己的重量wi 以及價值vi。檢驗礦產的流程是:見圖
若這批礦產的檢驗結果與所給標準值S 相差太多,就需要再去檢驗另一批礦產。小T不想費時間去檢驗另一批礦產,所以他想通過調整參數W 的值,讓檢驗結果盡可能的靠近標準值S,即使得S-Y 的絕對值最小。請你幫忙求出這個最小值。
輸入輸出
輸入描述 Input Description
第一行包含3個整數 n,m,S,分別表示礦石的個數、區間的個數和標準值。接下來的 n 行,每行2 個整數,中間用空格隔開,第i+1 行表示i 號礦石的重量wi 和價值vi 。接下來的 m 行,表示區間,每行2 個整數,中間用空格隔開,第i+n+1 行表示區間[Li,Ri]的兩個端點Li 和Ri。注意:不同區間可能重合或相互重疊。
輸出描述 Output Description
輸出只有一行,包含一個整數,表示所求的最小值。
樣例輸入 Sample Input
5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3
## 樣例
樣例輸出 Sample Output
10
數據范圍及提示 Data Size & Hint
解釋
當 W 選4 的時候,三個區間上檢驗值分別為20、5、0,這批礦產的檢驗結果為25,此時與標準值S 相差最小為10。
數據范圍
對于 10%的數據,有1≤n,m≤10;
對于 30%的數據,有1≤n,m≤500;
對于 50%的數據,有1≤n,m≤5,000;
對于 70%的數據,有1≤n,m≤10,000;
對于 100%的數據,有1≤n,m≤200,000,0 < wi, vi≤10^6,0 < S≤10^12,1≤Li≤Ri≤n。
思路
首先,我們可以知道每一個區間的價值都是遞減的:隨著W的增加,滿足條件的礦石數量減少,價值和減少,所以總的價值和也減少。
那么我們可以二分W(礦石最低重量),然后統計滿足條件的礦石的數量、價值的前綴和。
總時間復雜度O(mlogn)
代碼
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define mod 1000000007
#define ll long long
#define inf (1LL<<60)
using namespace std;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,m,mx;
ll S,ans=inf;
int l[200005],r[200005];
int w[200005],v[200005];
ll sum[200005],cnt[200005];
ll cal(int W)
{
ll tmp=0;
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1];
cnt[i]=cnt[i-1];
if(w[i]>=W)
{
sum[i]+=v[i];
cnt[i]++;
}
}
for(int i=1;i<=m;i++)
{
tmp+=(cnt[r[i]]-cnt[l[i]-1])*(sum[r[i]]-sum[l[i]-1]);
}
return tmp;
}
int main()
{
n=read();m=read();S=read();
for(int i=1;i<=n;i++)
w[i]=read(),v[i]=read();
for(int i=1;i<=n;i++)
mx=max(mx,w[i]);
for(int i=1;i<=m;i++)
l[i]=read(),r[i]=read();
int l=0,r=mx+1;
while(l<=r)
{
int mid=(l+r)>>1;
ll t=cal(mid);
ans=min(ans,abs(t-S));
if(t<S)r=mid-1;
else l=mid+1;
}
printf("%lld\n",ans);
return 0;
}
Mayan游戲
題目描述
Mayan puzzle是最近流行起來的一個游戲。游戲界面是一個 7 行5 列的棋盤,上面堆放著一些方塊,方塊不能懸空堆放,即方塊必須放在最下面一行,或者放在其他方塊之上。游戲通關是指在規定的步數內消除所有的方塊,消除方塊的規則如下:
1 . 每步移動可以且僅可以沿橫向(即向左或向右)拖動某一方塊一格:當拖動這一方塊時,如果拖動后到達的位置(以下稱目標位置)也有方塊,那么這兩個方塊將交換位置(參見輸入輸出樣例說明中的圖6 到圖7 );如果目標位置上沒有方塊,那么被拖動的方塊將從原來的豎列中抽出,并從目標位置上掉落(直到不懸空,參見下面圖1 和圖2);
2 . 任一時刻,如果在一橫行或者豎列上有連續三個或者三個以上相同顏色的方塊,則它們將立即被消除(參見圖1 到圖3)。
注意:
a) 如果同時有多組方塊滿足消除條件,幾組方塊會同時被消除(例如下面圖4 ,三個顏色為1 的方塊和三個顏色為 2 的方塊會同時被消除,最后剩下一個顏色為 2 的方塊)。
b) 當出現行和列都滿足消除條件且行列共享某個方塊時,行和列上滿足消除條件的所有方塊會被同時消除(例如下面圖5 所示的情形,5 個方塊會同時被消除)。
3 . 方塊消除之后,消除位置之上的方塊將掉落,掉落后可能會引起新的方塊消除。注意:掉落的過程中將不會有方塊的消除。
上面圖1 到圖 3 給出了在棋盤上移動一塊方塊之后棋盤的變化。棋盤的左下角方塊的坐標為(0, 0 ),將位于(3, 3 )的方塊向左移動之后,游戲界面從圖 1 變成圖 2 所示的狀態,此時在一豎列上有連續三塊顏色為4 的方塊,滿足消除條件,消除連續3 塊顏色為4 的方塊后,上方的顏色為3 的方塊掉落,形成圖 3 所示的局面。
輸入輸出格式
輸入格式:
輸入文件mayan.in
,共 6 行。
第一行為一個正整數n ,表示要求游戲通關的步數。
接下來的5 行,描述 7*5 的游戲界面。每行若干個整數,每兩個整數之間用一個空格隔開,每行以一個0 結束,自下向上表示每豎列方塊的顏色編號(顏色不多于10種,從1 開始順序編號,相同數字表示相同顏色)。
輸入數據保證初始棋盤中沒有可以消除的方塊。
輸出格式:
輸出文件名為mayan.out
。
如果有解決方案,輸出 n 行,每行包含 3 個整數x,y,g ,表示一次移動,每兩個整數之間用一個空格隔開,其中(x ,y)表示要移動的方塊的坐標,g 表示移動的方向,1 表示向右移動,-1表示向左移動。注意:多組解時,按照 x 為第一關健字,y 為第二關健字,1優先于-1 ,給出一組字典序最小的解。游戲界面左下角的坐標為(0 ,0 )。
如果沒有解決方案,輸出一行,包含一個整數-1。
輸入輸出樣例
輸入樣例#1:
3
1 0
2 1 0
2 3 4 0
3 1 0
2 4 3 4 0
輸出樣例#1:
2 1 1
3 1 1
3 0 1
說明
【輸入輸出樣例說明】
按箭頭方向的順序分別為圖6 到圖11
樣例輸入的游戲局面如上面第一個圖片所示,依次移動的三步是:(2 ,1 )處的方格向右移動,(3,1 )處的方格向右移動,(3 ,0)處的方格向右移動,最后可以將棋盤上所有方塊消除。
【數據范圍】
對于30% 的數據,初始棋盤上的方塊都在棋盤的最下面一行;
對于100%的數據,0 < n≤5 。
思路
如果某種顏色的方塊只剩下1個或兩個,則無解。
如果某個方塊的左邊是有方塊的,則放棄搜索該方塊向左移動的方案(此方案會在左邊方塊右移時被解決)
對于每個方塊:
- 若左右不相等,且可以右移,那么右移,并判斷合法性;
- 若左邊為空,且可以左移,那么左移,并判斷合法性。
代碼
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100
using namespace std;
int n;
struct tnode{int a[5][7];
int ss[11];
}q[maxn+10];
struct node{int x,y,g;}ans[6];
int sum=0;
bool f[5][7];
bool ok(int p)
{
int i,j,k;
for(i=0;i<=4;i++)if(q[p].a[i][0]!=0)return 0;
return 1;
}
bool xiao(int p)
{
int i,j,k;
for(i=1;i<=10;i++)
if(q[p].ss[i]==1 || q[p].ss[i]==2)return 0;
return 1;
}
void copy(int x,int y)
{
int i,j,k;
for(i=0;i<=4;i++)
for(j=0;j<=6;j++)
q[x].a[i][j]=q[y].a[i][j];
for(k=1;k<=10;k++)q[x].ss[k]=q[y].ss[k];
}
bool drop(int p)
{
int i,j,k;
bool flag=0;
for(i=0;i<=4;i++)
for(j=1;j<=6;j++)
if(q[p].a[i][j]!=0)
{
k=j;
while(k>0 && q[p].a[i][k-1]==0)
{
swap(q[p].a[i][k],q[p].a[i][k-1]);
flag=1,k--;
}
}
return flag;
}
bool xiaoqu(int p)
{
memset(f,0,sizeof(f));
int i,j,k;
bool flag=0;
for(i=0;i<=6;i++)
for(j=0;j<=2;j++)
if(q[p].a[j][i]!=0 &&
q[p].a[j+1][i]==q[p].a[j][i] &&
q[p].a[j+2][i]==q[p].a[j][i]
)
f[j][i]=f[j+1][i]=f[j+2][i]=1;
for(i=0;i<=4;i++)
for(j=0;j<=4;j++)
if(q[p].a[i][j]!=0 &&
q[p].a[i][j+1]==q[p].a[i][j] &&
q[p].a[i][j+2]==q[p].a[i][j]
)
f[i][j]=f[i][j+1]=f[i][j+2]=1;
for(i=0;i<=4;i++)
for(j=0;j<=6;j++)if(f[i][j])
{
k=q[p].a[i][j];
q[p].ss[k]--;
q[p].a[i][j]=0;
flag=1;
}
return flag;
}
bool dfs(int step,int p)
{
if(step>n)return ok(p);
if(!xiao(p))return 0;
int i,j,k,x,y;
for(i=0;i<=4;i++)
for(j=0;j<=6;j++)
{
if(q[p].a[i][j]==0)break;
ans[step].x=i,ans[step].y=j;
if(i<4 && q[p].a[i][j]!=q[p].a[i+1][j])
{
ans[step].g=1,sum++;
copy(sum,p);
swap(q[sum].a[i][j],q[sum].a[i+1][j]);
while(drop(sum) || xiaoqu(sum));
if(dfs(step+1,sum))return 1;
sum--;
}
if(i>0 && q[p].a[i-1][j]==0)
{
ans[step].g=-1,sum++;
copy(sum,p);
swap(q[sum].a[i][j],q[sum].a[i-1][j]);
while(drop(sum) || xiaoqu(sum));
if(dfs(step+1,sum))return 1;
sum--;
}
}
return 0;
}
int main()
{
int i,j,k;
scanf("%d",&n);
for(i=0;i<=4;i++)
for(j=0;j<=7;j++)
{
scanf("%d",&k);
if(k==0)break;
q[0].a[i][j]=k;
q[0].ss[k]++;
}
if(dfs(1,0))
for(i=1;i<=n;i++)
printf("%d %d %d\n",ans[i].x,ans[i].y,ans[i].g);
else printf("-1\n");
return 0;
}