【hnoi2008】

 


  首先:爲bzoj離我們而去感到十分傷感,對於所謂的“耒陽大視野培訓結構”感到十分不解。


    hnoi2008 總體來說沒有太多僞裝,模型比較明顯,但考的還是比較全(或者說偏), 同樣繼續延續的是數據比較水的特點=。=!
    一道一道來吧:
    可見直線:
    題意:略
    裸平面交,傻X的直接寫了朱澤園的排序增量算法了。
      
# include <cstdio>
# include <cstdlib>
# include <cmath>


using namespace std;


const double oo = 1e30, eps=1e-6;
const int maxn = 500000+100;
struct point {double x,y,z;};
struct line  {double a,b,c,k,d;int id;}lin[maxn];


bool p[maxn];
int tot,l,r,n,que[maxn];
inline bool bezero(double x){return x<eps&&x>-eps? true:false;};
inline double sqr(double x) {return x*x;};


void makeline(double zk,double zb)
{
lin[++tot]=(line){-zk, 1, -zb};
lin[tot].k= atan2(1, -zk)*1e9;
lin[tot].d= lin[tot].c/sqrt(sqr(lin[tot].a)+sqr(lin[tot].b));
lin[tot].id=tot;
}
int cmp(const void *i, const void *j)
{
line p=*(line *)i, q=*(line *)j;
if (p.k-q.k>eps||(bezero(p.k-q.k) && p.d-q.d>eps)) return 1;
else return -1;
}
inline bool stayout(line u, point v)
{
return (u.a*v.x+u.b*v.y+u.c*v.z<eps || bezero(v.z))?true:false;
}
inline point findpoint(line u, line v)
{
point g;
g.x= u.b*v.c-u.c*v.b;
g.y= u.c*v.a-u.a*v.c;
g.z= u.a*v.b-u.b*v.a;
if (!bezero(g.z)) g.x/=g.z,g.y/=g.z,g.z=1;
return g;
}
void work()
{
int i;que[1]=1; l=1;r=1;
for (i=2;i <=tot;i++)
if (!bezero(lin[i].k-lin[i-1].k))
    {
for (;l<r&&stayout(lin[i], findpoint(lin[que[r]], lin[que[r-1]]));r--);
for (;l<r&&stayout(lin[i], findpoint(lin[que[l]], lin[que[l+1]]));l++);
que[++r]= i;
}
for (;r-l>1&& stayout(lin[que[l]], findpoint(lin[que[r]], lin[que[r-1]]));r--);
for (;r-l>1&& stayout(lin[que[r]], findpoint(lin[que[l]], lin[que[l+1]]));l++);
}
int main()
{
int i; double zk, zb;
freopen("lines.in","r", stdin);
freopen("lines.out","w", stdout);
scanf("%d",&n);
for (i = 1; i <= n; i++)
{
scanf("%lf%lf", &zk, &zb);
makeline(zk,zb);
}
lin[++tot]=(line){1,0,oo,atan2(0,1)*1e9,oo};
lin[++tot]=(line){-1,0,oo,atan2(0,-1)*1e9,oo};
lin[++tot]=(line){0,-1,oo,atan2(-1,0)*1e9,oo}; 
//sort(1,tot);
qsort(lin+1, tot, sizeof(lin[1]), cmp);
work();
for (i = l; i <= r; i++) p[lin[que[i]].id]=true;
for (i = 1; i <= n; i++) if (p[i]) printf("%d ", i);
return 0;
}



  很傻X的一開始排序沒有把框框排進去,萎了一下(貌似不要框框=。=......,反正沒管,直接打模板了,還算短)


  明明的煩惱:
 題意: 給定一棵樹中某些節點的度數,另一些點的度數未知,求有多少棵樹滿足條件(不算點同構)


   n<= 1000的數據範圍讓人很不好搞,聯繫不到任何算法。
   後來在網上發現這個要用到prufer編碼。。。。。。。
   話說雖然寒假講過這個不過已經忘了一大半了,然後在維基上面重新看了一下。
   其實只需要用到prufer code 的幾個性質:
   第一: 一棵樹和一個prufer序列一一對應;
   第二:  數上點i在prufer序列中出現的次數 = i的度數減1
   第三: 一棵樹對應的prufer序列長度爲樹的點數-2
   對於已知度數的點,可以用prufer code 的公式或者直接用可重排列計算:
     令tot = sigma(已知度數di -1), 則爲: tot! / pai(已知度數di-1)!;
    然後序列中還有n-2 - tot的位置,可以任意選擇放誰。
    當然這樣是會少算了,最後還需要乘上C(n-2,tot);
   所以最後答案爲(tot! / pai(已知度數di-1)!) * C(n-2,tot) * (n^(n-2-tot));
   這裏涉及大整數乘除法, 相信沒有人願意去寫高精度除法,所以可以先分解質因數,就只要寫高乘了。
   
# include <cstdlib>
# include <cstdio>
# include <cmath>
# include <cstring>


using namespace std;


const int maxn = 1200, mo=100000;
int n,m,tot,ind[maxn],pr[maxn],d[maxn],have[maxn];
int step[maxn];
int ans[100000];
void prepare()
{
int i,j;
for (i = 2; i <= n; i++)
if (!step[i])
 for (j = i; j<=n; j+= i) step[j]= i;
step[2]=2;
}
void wfac(int x, int d)
{
for (int i = 1; i <= x; i++) have[i]+= d;
}
void release()
{
int i,j;
for (i = 1; i <= n; i++)
if (have[i])
for (j= i; j!= 1; j/= step[j])
 ind[step[j]]+= have[i]; 
}
void mul(int x)
{
int i;
for (i = 1; i <= ans[0]; i++) ans[i]*= x;
for (i = 1; i <= ans[0]-1; i++)
 if (ans[i]>=mo) ans[i+1]+= ans[i]/mo, ans[i]%= mo;
for (;ans[ans[0]]>=mo;ans[0]++) ans[ans[0]+1]= ans[ans[0]]/mo, ans[ans[0]]%= mo;
}


int main()
{
int i,j;
freopen("tree.in","r", stdin);
freopen("tree.out","w", stdout);
scanf("%d", &n); ans[0]=1; ans[1]=1;
for (i = 1; i <= n; i++)
{
scanf("%d", &d[i]);
if (d[i]!=-1) tot+= d[i]-1, m++;
}
prepare();
wfac(n-2,1);  wfac(n-2-tot,-1);
for (i = 1; i <= n; i++) 
 if (d[i]!=-1) wfac(d[i]-1, -1);
have[n-m]+= n-2-tot;
release();
for (i = 1; i <= n; i++)
 for (j = 1; j <= ind[i]; j++)
   mul(i);
printf("%d", ans[ans[0]]);
for (i = ans[0]-1;i >= 1; i--)
    printf("%05d", ans[i]);
return 0;
}



越獄:
題意:略
放到noip都不爲過。
# include <cstdlib>
# include <cstdio>
# include <cmath>
# include <cstring>


using namespace std;
const int mo = 100003;


long long m,n;
long long mul(long long a, long long b)
{
long long c = 1;
for (;b>0;b>>=1,a=(a*a)%mo)
 if (b&1) c=(c*a)%mo;
return c;
 
}
int main()
{
freopen("prison.in","r", stdin);
freopen("prison.out","w", stdout);
scanf("%I64d%I64d", &m, &n);
printf("%I64d", (mul(m,n)-(m*mul(m-1, n-1))%mo+mo)%mo);
return 0;
}



答案輸出時候一開始沒加mo,輸出了負數,這些細節一定要注意。


神奇的國度:
題意:求弦圖的最小染色數。
通過baidu發現,這個圖其實是一個弦圖,然後通過指引去看了陳丹琦的《弦圖和區間圖》,猛然發現又是一個猛丟定義的ppt。。。。。。 看了很久終於看到了這道題=。=,結果被完美的用完美消除序列一筆帶過了。。。。。。
然後一生氣,模仿最大勢算法寫了一個mlogn的貪心染色,結果A了。。。。。。
先隨便選取一個點染色,那麼它周圍的點必須和它顏色不同,所以周圍的點標號++, 然後貪心每次選取標號最大的點染色(因爲以後這個點如果現在不染色以後的標號會更大),直到點全部取完。可以理解:選點過程中,點的最大度數+1就是答案(假設這個點爲i,度數爲di,那麼它必須和前面di個點顏色不同,所以染好i點至少要用di+1中顏色)。
最大勢算法原本可以做到o(n+m),但是那個鏈表實在太醜了,就直接用線段樹裸求。
# include <cstdlib>
# include <cstdio>
# include <cmath>
# include <cstring>


using namespace std;


const int maxn = 10000+5, maxm = 1000000+5;
int top, next[maxm*2], linke[maxm*2], point[maxm*2];
int te[maxn*4], st, d[maxn];
int n,m, ans;
void link(int x, int y)
{
++top; next[top]= linke[x]; linke[x] = top; point[top] = y;
}


inline int max(int x, int y){ return d[x]>d[y]?x:y;};
void updata(int x)
{
for (x=x+st>>1;x >= 1; x>>=1)
  te[x] = max(te[x<<1],te[(x<<1)+1]); 
}
int main()
{
int ke,g,i,j, x, y;
freopen("kingdom.in", "r", stdin);
freopen("kingdom.out", "w", stdout);
scanf("%d%d", &n, &m);
for (i = 1; i <= m; i++)
scanf("%d%d", &x, &y),link(x, y),link(y, x);
for (i = 1; i <= n; i++) d[i] = 1;
for (st = 1; st <= n; st<<=1);
memset(te, 0, sizeof(te));
    for (i = 1; i <= n; i++) te[i+st] = i;
    for (i = st-1; i >= 1; i--) te[i] = max(te[i<<1], te[(i<<1)+1]);
for (i = 1, j = 1; i <= n; i++)
{
g = te[1]; ans = ans<d[g]?d[g]:ans; 
d[g]=-1; updata(g); 
for (ke = linke[g]; ke!= 0; ke=next[ke])
 if (d[point[ke]]>0)
  d[point[ke]]++, updata(point[ke]);
}
printf("%d", ans);
return 0;
}




gt考試:
題意:略;
簡單的kmp + 矩乘dp;
poj上有一道AC自動機 + 矩乘dp, 以前也寫了lzn的kmp+dp,所以這道題想起來無壓力。
寫構造矩陣時一開始寫頹了(......), 細心,細心。
# include <cstdlib>
# include <cstdio>
# include <cmath>
# include <cstring>


using namespace std;


const int maxs = 25;


int n,m,mo,ans;
char c[maxs];
int next[maxs], f[maxs][maxs], g[maxs][maxs], p[maxs][maxs];


void prepare_kmp()
{
int i,j,d;
c[m+1]=1;
for (next[1]=0,j=0,i=2; i <= m; i++)
{
 for (;c[j+1]!= c[i] && j!=0;j=next[j]);
 if (c[j+1]==c[i]) next[i]=++j;
}
for (i = 0; i < m; i++)
 for (d = 0; d <= 9; d++)
 {
for (j = i; j!= 0&&c[j+1]!= d+'0'; j=next[j]);
if (c[j+1]==d+'0') g[i][j+1]++; else g[i][0]++;
 }
}


void mul(int c[maxs][maxs], int a[maxs][maxs], int b[maxs][maxs])
{
int i,j,k;
memset(p,0,sizeof(p));
for (i = 0; i <= m; i++)
 for (j = 0; j <= m; j++)
   for (k = 0; k <= m; k++)
     p[i][j] = (p[i][j]+a[i][k] * b[k][j]) % mo;
for (i = 0; i <= m; i++)
 for (j = 0; j <= m; j++)
     c[i][j]= p[i][j];
}
int main()
{
int i,j;
freopen("gt.in","r", stdin);
freopen("gt.out","w", stdout);
scanf("%d%d%d\n", &n, &m, &mo);
for (i = 1; i <= m; i++) 
scanf("%c", &c[i]);
    prepare_kmp();
    for (i = 0; i <= m; i++)
      for (j = 0; j <= m; j++)
        f[i][j]= g[i][j];
    for (n--;n > 0;n>>=1,mul(g,g,g))
      if (n & 1) mul(f,f,g);
    //for (n--; n > 0; n--) 
 // mul(f,f,g);
    ans = 0;
    for (i = 0; i < m; i++) ans=  (ans+f[0][i]) % mo;
    printf("%d", ans);
return 0;
}



洗牌:
burnside。。。。。。。。入棧。
見另一篇blog

玩具裝箱:
題意:略。
這道題是水過去的,都不好意思講了,大家都說用斜率優化dp,結果化啊化啊暈掉了,然後心存僥倖的用單調指針維護了(l-L)^2的最大位置。
然後只在這個位置附近枚舉,本來只是想看看數據水不水的,結果事實又一次證明,hnoi數據很水。。。。。。


遙遠行星:
題意:略
這裏強烈吐槽一下,我自己寫了N多方法,每一個精度都很頹=。=!......
後來只得屈服。。。。。。看了網上的題解,照着上面的思路推了一下。
f[I ] 表示I 星球所受力的大小,轉移時,500以內的星球裸做,500以外的用f[I-500]估算,網上到處都有推算,這裏不寫了。
# include <cstdlib>
# include <cstdio>
# include <cmath>


using namespace std;


const int maxn = 100000+5;
double tm[maxn], mass[maxn],f[maxn];
int j,n,i,t, g[maxn];
double a;


int main()
{
freopen("planet.in","r", stdin);
freopen("planet.out","w",stdout);
scanf("%d", &n);
scanf("%lf", &a);
for (i = 1; i <= n; i++)
{ 
  scanf("%lf", &mass[i]);
  tm[i] = tm[i-1] + mass[i];
}
t = 500;
for (i = 2; i <= n; i++)
{
g[i] = floor(a*i);
if (i <= t) 
{
for (j = 1; j <= g[i]; j++)
 f[i]+= mass[i]*mass[j]/(i-j);
}
else
{
f[i] = (f[i-t] /mass[i-t])* (i- t - g[i-t]/2) / (i - g[i-t]/2);
for  (j = g[i-t]+1; j <= g[i]; j++)
 f[i] += mass[j]/(i-j);
f[i]*= mass[i];
}
}
for (i = 1; i <= n; i++)
 printf("%.6lf\n", f[i]);
return 0;
}



hnoi2008 的模型都隱藏不深,但是一定要細心細心再細心,不要因爲題目偏水而意外丟分,還有,hnoi的數據又一次告訴我們,沒辦法時一定要騙分。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章