Day1
T1 積木大賽
NOIp2013D2T1.....看到的時候我還以為我記錯了,以為原題是一次可以隨便加,這題只能加一,出考場后查了下發現一模一樣。
#include <iostream>
#include <cstdio>
using namespace std;
const int N=100010;
int n,d[N],ans;
int main()
{
int i;
scanf("%d",&n);
for (i=1;i<=n;++i)
{
scanf("%d",d+i);
if (d[i]>d[i-1])
{
ans+=d[i]-d[i-1];
}
}
cout<<ans;
return 0;
}
T2 貨幣系統
去掉可以由其它貨幣拼成的貨幣,這個結論倒是很快猜到了。但由於xkdyh留下的陰影,一開始我還寫了個exgcd...幸好大樣例比較良心,有一組數據是三種貨幣拼成另一種。然后再仔細一看,發現是個完全背包...
簡單證明一下:
結論:對於一個沒有任何一種貨幣可以由系統內其它貨幣拼成的貨幣系統 \((n,A)\),與其等價的貨幣系統 \((m,B)\) 只能是 \((n,A)\) 自身或者加上一些能由 \((n,A)\) 表示的數。
若 \(A\not\subseteq B\),任取 \(t\in (A-B)\),那么在 \(B\) 中必然有一些元素能夠拼成 \(t\),而這些元素在 \(A\) 中必然有不能表示的(否則與 \(A\) 中沒有任何一種貨幣可以由系統內其它貨幣拼成矛盾),而存在 \((m,B)\) 能表示而 \((n,A)\) 不能表示的數與 \((n,A),(m,B)\) 等價矛盾,不成立。
若 \(B\) 中有 \((n,A)\) 所不能表示的元素,依然與 \((n,A),(m,B)\) 等價矛盾,不成立。
所以命題得證。
排個序然后完全背包去掉能被系統內其它貨幣表示的貨幣即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int t,n,a[110],maxx;
bool f[25010];
int main()
{
int i,j,ans;
scanf("%d",&t);
while (t--)
{
scanf("%d",&n);
ans=n;
maxx=0;
for (i=1;i<=n;++i)
{
scanf("%d",a+i);
maxx=max(maxx,a[i]);
}
sort(a+1,a+n+1);
memset(f,false,sizeof(f));
f[0]=true;
for (i=1;i<=n;++i)
{
if (f[a[i]])
{
--ans;
continue;
}
for (j=0;j+a[i]<=maxx;++j)
{
if (f[j])
{
f[j+a[i]]=true;
}
}
}
printf("%d\n",ans);
}
return 0;
}
T3 賽道修建
看到這題就想起了ylh當時跟我一個房間的時候切掉的 CF div.2 E,但賽后發現不一樣...
出考場得知dew、ylh都切掉了這題,然而我只寫了直徑、鏈和菊花圖的 \(55\) 分...涼涼涼
Day2
T1 旅行
一開始看錯題了,以為是最小字典序生成樹,還在想為什么 \(m\) 這么小..然后仔細一看題,發現一條邊只能回溯時重復經過,也就是最后得到的序列只能是個dfs序...數據范圍很小,所以就枚舉斷邊寫了個 \(O(n^2)\) 的,預處理邊排序。然后出考場聽一堆dalao在那說各種 \(O(nlogn)\),\(O(n)\) 做法...都不會QAQ
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=5010;
void dfs(int u);
int n,m,cut,a[N][N],tot,e[N][2];
bool vis[N],used[N]; //used用於對m個字典序取min,若used[i]=true說明斷開第 i 條邊時一定不是答案
vector<int> g[N];
int main()
{
int i,j,u,v,minn;
scanf("%d%d",&n,&m);
for (i=0;i<m;++i)
{
scanf("%d%d",&u,&v);
e[i][0]=u;
e[i][1]=v;
g[u].push_back(v);
g[v].push_back(u);
}
for (i=1;i<=n;++i)
{
sort(g[i].begin(),g[i].end());
}
if (n==m)
{
for (cut=0;cut<m;++cut)
{
memset(vis,false,sizeof(vis));
tot=0;
dfs(1);
if (tot<n)
{
used[cut]=true;
}
}
for (i=1;i<=n;++i)
{
minn=n;
for (j=0;j<m;++j)
{
if (!used[j]&&a[j][i]<minn)
{
minn=a[j][i];
}
}
for (j=0;j<m;++j)
{
if (a[j][i]>minn)
{
used[j]=true;
}
}
printf("%d",minn);
if (i<n)
{
putchar(' ');
}
}
}
else
{
cut=m;
dfs(1);
for (i=1;i<=n;++i)
{
printf("%d",a[m][i]);
if (i<n)
{
putchar(' ');
}
}
}
return 0;
}
void dfs(int u)
{
if (vis[u])
{
return;
}
vis[u]=true;
a[cut][++tot]=u;
int v,i;
for (i=0;i<g[u].size();++i)
{
v=g[u][i];
if ((u!=e[cut][0]||v!=e[cut][1])&&(u!=e[cut][1]||v!=e[cut][0]))
{
dfs(v);
}
}
}
T2 填數游戲
要是數據范圍給到 \(10^9\) 我就不會在考場上推半天了...一開始想了好久怎么 \(O(nm)\) dp,雖然沒想出來怎么做,但發現了暴力怎么寫:一種方案合法等價於:對於每個點,它右邊的點先往下再往右的路徑小於它下面的點先往右再往下的路徑。因為這兩條路徑分別是一個點向右走后最大的路徑和向下走后最小的路徑。然后打了個表,發現 \((n,m)=(n,n+1)\times 3^{m-n-1} (n\ge 2,m\ge n+1)\)。於是開始跑 \((8,9)\) ,跑到11:50 還沒跑出來...幸好發現了 \((n,n)\) 和 \((n,n+1)\) 之間也有一定的規律,把 \((8,9)\) 算出來了...
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const long long Ans[9][10]={{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,4,12,36,0,0,0,0,0,0},
{0,8,36,112,336,0,0,0,0,0},
{0,16,108,336,912,2688,0,0,0,0},
{0,32,324,1008,2688,7136,21312,0,0,0},
{0,64,972,3024,8064,21312,56768,170112,0,0},
{0,128,2916,9072,24192,63936,170112,453504,1360128,0},
{0,256,8748,27216,72576,191808,510336,1360128,3626752,10879488}}; //其實有一部分表是不必要(沒有意義)的...
const long long M=1000000007;
long long n,m,ans=1;
int main()
{
int i;
cin>>n>>m;
if (n>m)
{
swap(n,m);
}
if (n==1)
{
for (i=30;i>=0;--i)
{
ans=ans*ans%M;
if (m&(1<<i))
{
ans=ans*2%M;
}
}
cout<<ans;
}
else
{
if (m<=n+1)
{
cout<<Ans[n][m];
return 0;
}
else
{
for (i=30;i>=0;--i)
{
ans=ans*ans%M;
if ((m-n-1)&(1<<i))
{
ans=ans*3%M;
}
}
cout<<ans*Ans[n][n+1]%M;
}
}
return 0;
}
T3 保衛王國
據說是ddp...考場上先10min寫了44分(一開始還以為是55分Orz),然后看了下,覺得B1挺可寫的,更新向上的鏈貌似就可以了,但最后沒調出來..
Day7
上(tui)了一個星期的whk..個鬼啊,三天在考期中,就上了兩天whk。感覺從零開始的whk沒有想象中那么恐怖...
一周不讓去機房,一到家就在洛谷上測了一下公布的代碼.其它題都和預估的一樣,D2T1可能會被卡常,洛谷上開了O2最慢點 \(0.9s\) ,而且不用vector好像也過不了,不知道是不是洛谷上內存開小了的原因...
D1T3大眾AC題我爆菊(花圖)了... 幸好菊花圖的數據分治放在了最后面,還有 \(40\) 分。發現自己傻了,不知道為什么會認為只有最短的兩條邊可以拼在一起,其它邊都只能自成一條道.......
聽說D2T3不用ddp,還是我太菜了...
估分:\(100+100+40+88/100+100+44=472/484\)
Day10
GGF咕咕咕,然而我把兩個T3寫了一下..
D1T3真的好簡單...二分答案,check的時候dfs處理每棵子樹並返回塊數最多時最大剩余,具體就是把子樹返回值排個序,雙指針配對得到最多塊數,然后從最后一個配對的左指針開始往前這么多個依次配對,最后看剩下的沒配對的里面最大的是多少。(第二天換成CCF數據發現做法掛了)處理子樹的時候二分返回值不影響復雜度但能保證正確性。
D2T3做法挺有趣的..倍增題做少了,估計做多了就比較套路了...預處理出 \(f[u][1],f[u][0],g[u][1],g[u][0]\),分別代表選/不選 \(u\) 時 子樹 \(u\) 答案,選/不選 \(u\) 時 \(u\) 往上(整顆樹減去子樹 \(u\))的答案。倍增處理出祖先 \(fa[u][i]\) 表示 \(u\) 的 \(2^i\) 祖先,用 \(bz[u][i][0/1][0/1]\) 表示子樹 \(fa[u][i]\) 除去子樹 \(u\) ,其中 \(u\) 選/不選,\(fa[u][i]\) 選/不選的答案,可以在dfs預處理 f 和 g 的同時算出 \(bz[u][0][0/1][0/1]\) ,然后 \(bz[u][i][a][b]=\min(bz[u][i-1][a][0]+bz[fa[u][i-1]][i-1][0][b],bz[u][i-1][a][1]+bz[fa[u][i-1]][i-1][1][b])\) 。計算答案的時候如果是祖先關系直接倍增計算鏈上答案,再加上子樹的 f 和 祖先上方的 g;否則倍增到 \(lca\) 計算路徑上的答案,兩棵子樹以及 \(lca\) 上方的答案就是對應的 \(f\) 和 \(g\) 。然后寫到 \(22:15\),交上去 \(68\) 分,回寢室...ab相鄰的 \(16\) 分真的好簡單,不用倍增,考場上應該寫出來的...
Day11
剛到學校聽說自己 \(480\) ,還在想8700k這么強,能把我的 travel 卡成 \(96\) ...
中午一看是 \(489\),數據真有趣...D1T3 隔壁原 \(95\) 變成 \(80\),我昨天A的變成 \(90\) 了,考場寫的還騙到了 \(5\) 分(\(45\))...8700k天下第一!