2017年第23屆全國青少年資訊學奧林匹克競賽分割槽聯賽提高組複賽題解報告
2017年第23屆全國青少年資訊學奧林匹克競賽分割槽聯賽複賽提高組題解報告
山西現代雙語學校南校
劉鍇睿
Day1
第一題:小凱的疑惑
這道題拿上以後,就很悶,十幾年的送分模擬題,成了數學題,考場上蒙B的我沒思路就先拿樣例手動模擬了一下,用了半個小時莫名奇妙用小學奧數亂搞推出了ans=a*b-a-b以後明明是正解,直接輸出ans即可。
標準證明思路:
https://www.zybang.com/question/000b3c26c0897ade29aecae936715fee.html
(但是
聽監考老師說要跑反作弊分析系統,所以對30%和60%的點用迴圈寫了一遍,好像是for (int i=a*b-1;;i–){…}這樣的,反正也能過吧。
)時間複雜度O(1)
但是考場上推出來以後我想,按照初賽提高組的樣子,不應該這麼簡單,在看看,於是就又推了一下發現,設他的兩種面值為a,b交換使得a< b,設他用的a,b的數量為na,nb那麼又有na,nb是兩個迴圈數列,且數列na的迴圈週期為b,nb的迴圈週期為a,這樣又有ans<=ab,寫一個for,當出現迴圈時即為ans。時間複雜度O(n)。
因為ans< ab所以從ab+50開始倒著往回求餘,首次結果!=0時即為ans。時間複雜度O(n)
但是考場上的我怎麼敢寫呢,因為證明出ans<=ab,用了大暴力,不知道能過多少個測試點(據說是大資料要開ll“五年OI一場空,沒開long long見祖宗” )。考完以後想問我考的是NOIP(全國青少年資訊學奧林匹克聯賽(National Olympiad in Informatics in Provinces,簡稱NOIP))還是NOM(math)P。
第二題:時間複雜度
NOIP出題人莫名其妙把第二題搞了一個模擬,跟2008年pj的立體圖有異曲同工之妙,不過蒟蒻的我沒打,於是直接打了30分保證前二分之一行全是F的資料的程式碼然後還寫了一個若不符合30分資料的特殊性質則輸出YES(根據隨機測試資料生成的結果看yes比err和no出現概率大的多)不過多組資料理論上是過不了。回來以後看了題解,就是一個模擬。
第三題:逛公園
寫到第三題的時候大概是11點左右,當時也感覺到了CCF一般會把dp放在Day1,第二題純模擬應該不是(就算是蒟蒻也不會),第三題怎麼看也看不出來,知道第三題拿不了分隨便寫了一個SPFA求最短路+DFS,第三題放這麼簡單的題,明顯資料就有詐,但我還是打了,誰讓我們山西分數低去年240省一(\手動開心),民間資料據說很弱。
標答:拓撲排序+DP。好像spfa+DP也是可以的。
Day2
第一題:乳酪
看完以後只想說NOIP可能真的是不小心把題目給發反了(還記得內兩天的解壓密碼嗎)空間座標系,看了一下資料範圍擦邊int,用longlong可以但是相加以後保不準要超longlong,最後證明的確會爆,所以我寫了一個
ll r;
ll x,y,z;
cin ......;
r*=r;
int ok=0;
ll dist=pow((x1-x2),2)
if(r<=dist)ok=1;
if(ok==0)dist+=pow((y1-y2),2);
//......
//同理不寫
if(ok==1)mp[][]=mp[][]=1;
想分流一下結果把if裡的r和dist的比較符號寫反了,徹底爆炸。D1T1“是五年OI一場空,沒開long long見祖宗”這個開了ll都不行還要BigInteger。
第二題:寶藏
一看題,為什麼n那麼小(<=12),那麼多條邊,正常思路都是重邊,就我一看覺得是稠密圖開開心心的寫了一個鄰接矩陣,心裡還想真好寫比vector的連結串列好多了,後來才知道有重邊。考前就在說肯定會有DP。因為n<=12,狀壓DP,網上看到有現成的程式碼,就直接上了,時間複雜度
其實根據狀壓dp那麼多個if的特性肯定是不會爆的,可是我非得頭鐵,要搞到,結果搞成了,但是發現錯的,然後就不改了。為此浪費了我45分鐘時間
有人說最小生成樹好像也可以,大資料也貌似可以過。五中有一個考前說要先打一個打最小生成樹的模板的還真中了,不過也有人說過不了。
標答:狀壓dp
別人的程式碼:
#include<bits/stdc++.h>
using namespace std;
int bo[5020][22];
int f[5020];
int n;
int edge[22][22];
int m;
int ans=0x7ffffff;
int dfs(int root)
{
memset(bo,0,sizeof(bo));
memset(f,63,sizeof(f));
f[1<<(root)] = 0; //初始化
bo[(1<<root)][root] = 1;
int N = 1<<n;
for(int i=0;i<N;i++)//列舉點的狀態
for(int j=0;j<n;j++)if((i&(1<<j))>0) //j點能到達
for(int k=0;k<n;k++)if(((i&(1<<k))==0)&&(edge[j][k]!=-1))//k點不能到達,且jk之間有邊
{
int p = (1<<k);
int x = f[i] + bo[i][j] * edge[j][k]; //計算費用,方程
if(f[i+p]>x)
{
f[i+p] = x;
for(int l=0;l<n;l++) bo[i+p][l]=bo[i][l];
bo[i+p][k]=bo[i][j]+1;
}
}
return f[N-1];
}
int main()
{
memset(edge,-1,sizeof(edge));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(edge[x-1][y-1]>z || edge[x-1][y-1]==-1) //記得直接選權值最小的邊就可以了
edge[x-1][y-1]=edge[y-1][x-1]=z; //直接清除重邊。
}
for(int i=0;i<n;i++) //以i為出發點。
ans = min(ans,dfs(i));
printf("%d\n",ans);
}
第三題:列隊
知道第三題做不出來的我直接打了測試點1,2,3,4,5,6,11,12,13,14
不知道要錯幾個。
正解:splay/樹狀陣列
總結
可以看出近幾年NOIP的出題者一直在尋求革新(那你也不能把D1T1搞成小學數學呀),從初賽提高組到普及組到現在的複賽提高組,難度也在波動,我們不能說NOIP怎麼樣,只能不斷去適應他,雖然有很多遺憾的失誤,但也沒辦法了,安心滾回去高考吧,也希望大雙語的OI可以從此崛起,雙語的同學不是不行,而是缺少一個發現自己的機會,與教學資源。