遊老師模擬賽5 27

2021-08-20 07:27:40 字數 3925 閱讀 5420

1.tower

【題目描述】

平面上有n個整點。如果將點(x0,y0)移動到(x1,y1),則需要的代價為|x0-x1|+|y0-y1|。求使得k(k=1…n)個點在同一位置上最少需要的代價。

【輸入檔案】

第一行1個正整數n ;

接下來n行,每行兩個正整數xi和yi,為第i個點的座標,不超過106。

【輸出檔案】

輸出共n行,第i行為使得有i個點在同一位置的最少代價。

【樣例輸入】

4 15 14

15 16

14 15

16 15

【樣例輸出】

0 2

3 4

【資料規模】

對於100%的資料,滿足1≤n≤50。

最優解必定是在(xi,yj)這些位置上的,因此只要列舉這些位置(共n2個),然後將所有點與這個位置的距離d[i]從小到大排序,然後s[i]=d[1]+d[2]+…+d[i]。那麼ans[k]=min。

#include

using

namespace

std;

int n,i,j,ans[52],x[52],y[52],k,d[52],sum;

int main()

for (i=1;i<=n;i++) printf("%d\n",ans[i]);

}

2.置換

題目描述:

n個數字,初始序列為1、2、3、4……n。現有m行n列的數字,每行為乙個置換操作。如某行操作的第i個數字為a[i],那麼就把原來序列中的第a[i]個數字放到現在這個序列的第i的位置上,然後組成新的序列。然後,看列表的第二行操作……、第三行操作……一直到最後一行操作,重複上面的操作。當最後一行的操作結束,組成了的序列又按照第一行來操作,然後第二行操作……第三行操作……一直迴圈下去,直到一共操作了k行為止。最後生成的這個序列就是我們最終的序列。

輸入格式:

第一行三個數,n,m和k。

接下來m行,每行n個數。

輸出格式:

一行,一共n個數,表示最終的禮品序列。n個數之間用乙個空格隔開,行尾沒有空格,需要回車。

輸入樣例:

7 5 8

6 1 3 7 5 2 4

3 2 4 5 6 7 1

7 1 3 4 5 2 6

5 6 7 3 1 2 4

2 7 3 4 6 1 5

輸出樣例:

2 4 6 3 5 1 7

資料範圍:

1<=n<=100;1<=m<=10;1<=k<=2^31-1。

對於50%的資料,保證k<=500。這些資料每個資料點8分,其他的資料每個資料點12分。

置換是迴圈往復的,並且我們可以發現雖然k很大,但是m<=10,那麼我們可以先預處理出a[i][j]表示第i次置換以後位置j上是原遞增序列中的哪乙個數,然後把k分成兩部分:k/m 和k%m現在把m次置換稱為乙個大置換,那大置換的次數就是k/m次,剩下的k%m次是依次置換。由於我們一開始已經預處理出每次置換乙個以後的序列,所以可以把a[m][1..n]和a[k%m][1..n]提取出來建立兩個矩陣matrix1、matrix2,設初始的序列(即遞增序列)ans,最終的序列就是ans*(matrix1^(k/m))*matrix2,注意順序不能顛倒,筆者就被這個坑了好久。。。。matrix1的k/m次方用快速冪求。

時間複雜度:o(n^3logk)

#include

using

namespace

std;

int n,m,k,i,j,x,t;

struct maa[12],b,c;

int read()while(c<48||c>57);

do x=(x<<1)+(x<<3)+(c^48),c=getchar();while(c>=48&&c<=57);

return f*x;

}ma operator *(ma x,ma y)

ma operator ^(ma x,int y)

void print(ma a,int x,int y)

int main()

for (i=1;i<=n;i++) b.a[1][i]=i;

for (i=1;i<=m;i++) c=c*a[i];

b=b*(c^(k/m));

for (i=1;i<=k%m;i++) b=b*a[i];

print(b,1,n);

}

jxy大佬不用矩陣,用乙個類似矩陣乘法的操作做到了o(nlogk),orz

#include

int n,m,k,tmp,i,j;

struct maa[12],ans;

ma operator *(ma x,ma y)

void calc(int num)

calc(num>>1);

ans=ans*ans;

if (num&1) ans=ans*a[m];

}int main()

calc(k/m);

tmp=k%m;

for (i=1;i<=n;i++) printf("%d ",ans.a[a[tmp].a[i]]);

}

3.數列

【題目描述】

給定乙個長度是n的數列a,我們稱乙個數列是完美的,當且僅當對於其任意連續自序列的和都是正的。現在你有乙個操作可以改變量列,選擇乙個區間[x,y]滿足ax+ax+1+…+ay<0,1小於x<=y小於n,令s=ax+ax+1+…+ay,對於ax-1和ay+1分別加上s,ax和ay分別減去s(如果x = y就減兩次).問最少幾次這樣的操作使得最終數列是完美的。

【輸入檔案】

第一行乙個數n,以下n個數。

【輸出檔案】

乙個數表示最少的操作次數,如果無解輸出-1。

【樣例輸入】

5 13

-3 -4

-5 62

【樣例輸出】

2 【樣例解釋】

首先選擇區間[2,4],之後數列變成1, 9, -4, 7, 50,然後選擇[3,3],數列變成1, 5, 4, 3, 50

【資料規模】

對於20%的資料,滿足1≤n≤5;

對於100%的資料,滿足1≤n≤105;1≤ | a[i] | ≤ 231-1。

首先乙個滿足條件的序列就是乙個字首和嚴格遞增的數列,

然後我們可以發現乙個重要的性質:操作[x,y]就是交換x-1和y的字首和!於是問題就簡單了:用最少的交換次數將字首和數列排序。這個問題可以通過簡單的找環解決。首先對問題進行離散化,然後看當前的置換中有幾個圈,如果有k個,那麼答案就是n-k。注意判斷無解的情況。

#include

using

namespace

std;

typedef

long

long ll;

const

int n=100003;

struct kka[n];

int i,x,ne[n],t,n,ans;

int read()while(c<48||c>57);

do x=(x<<1)+(x<<3)+(c^48),c=getchar();while(c>=48&&c<=57);

return f*x;

}bool cmp(kk x,kk y)

a[i].sum=a[i-1].sum+x;

a[i].id=i;

}if (a[n].sum<=0)

sort(a+1,a+n,cmp);

for (i=1;ifor (t=a[i].id;ne[t];t=ne[t]);

if (t!=i) ans++,ne[i]=t;

}printf("%d",ans);

}

NOIP模擬賽 老師

題目描述 一座有n層的教學樓裡有一些學生,第i 0 i n 層有studentsi個學生。你被給定了乙個數k,如果第i層有x個學生,那麼這一層需要 x k 個老師。你可以調整每個學生的樓層,但是每個學生至多只能調整一層,就是說第i層的學生只能去第i 1層 如果有的話 第i層 第i 1層 如果i 1 ...

5 27模擬題 擷取

給你n段線的長度,現在要你從他們上面切下k段來 切下的不能合併 使得這k段長度相等,並且最大。如若求出的答案小於0.01,則認為無解,輸出0.00。所有非整數都精確到了兩位,輸出亦然 1 n,k 10000 每條線段的長度都是在1到100000之間的實數 二分查詢,一般用於最小值的最大值,最大值的最...

5 27 女生賽總結

好的吧,真的是有史以來第一次出去比賽 好像大家都也還是給予厚望的,女生賽。前面過了兩個其實還好吧 最後卡,卡到兩三個題後來還是出不來了。我們刻意避開了 打鐵關 但是還是打鐵了qaq,zj說dp的方程明明都寫對了啊。他們t了那是因為暴力,這種錯法我還是第一次見啊。去之前自己還帶了自己列印的1 3部落格...