【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的数据又一次告诉我们,没办法时一定要骗分。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章