SDUACM暑期集训周赛(五)

来源:互联网 发布:安装包 制作软件 编辑:程序博客网 时间:2024/06/14 12:09

BZOJ 2186 沙拉公主的困惑

[Problem]

询问n!中与m!互质的个数(n > m)

[Solution]

ans = n! / m ! * phi(m!)

显然[1, m!] 此区间中有phi(m!)个与m!互质的数

[m! + 1, m! + m!] 同样有phi(m!)个 gcd(m! + i, m !) = gcd(i, m!)

[2 * m! + 1, 3 * m!] phi(m!) 个
…….

[Code]

#include <cstdio>  #define ll long long  #define maxn 10000000  #define maxk 1000000  using namespace std;  ll p[maxk], R, inv[maxn+100], N, M, tot, fac[maxn+100],      ans[maxn+100], T;  bool no[maxn+100];  void getinv()  {      int i, m=(R<maxn?R:maxn); inv[1]=1;      for(i=2;i<m;i++)inv[i]=(ll)(R-R/i)*inv[R%i]%R;  }  void shai()  {      int i, j;      for(i=2;i<=maxn+1;i++)      {          if(!no[i])p[++tot]=i;          for(j=1;i*p[j]<=maxn;j++)          {              no[i*p[j]]=true;              if(i%p[j]==0)break;          }      }  }  void getfac()  {      int i;      fac[1]=1;      for(i=2;i<=maxn;i++)fac[i]=fac[i-1]*i%R;  }  void getans()  {      int i, j;      ll t;      ans[1]=1;      for(i=1;i<=tot;i++)      {          t=(ll)ans[p[i]-1]*(p[i]-1)%R*inv[p[i]%R]%R;          for(j=p[i];j<p[i+1];j++)ans[j]=t;      }  }  int main()  {      int i;      scanf("%d%d",&T,&R);      shai();      getinv();      getfac();      getans();      while(T--)      {          scanf("%d%d",&N,&M);          printf("%lld\n",fac[N]*ans[M]%R);      }      return 0;  }  

BZOJ 2186 POJ 2992 Divisors

[Solution]
把每个数字分解质因数,然后把结果存到数组中,前缀和累加一下,对于计算cnm时用幂进

行运算

[Code]

#include<cstdio>#include<iostream>#include<cstring>using namespace std;#define N 1000int p[500], top = 0, ans[500];int num[N][N], n, m, sum[N][N];bool isp[N];long long f[N][N];void cal(int n, int m){    for(int i = 1; i <= top; i++)  ans[i] = 0;    if (n - m < m) m = n - m;    if (f[n][m]) return;    for(int j = 1; j <= top; j++) {        ans[j] = sum[n][j] - sum[m][j] - sum[n - m][j];    }}void init(){    for(int i = 2; i <= 431; i++) {        if (isp[i])  continue;        p[++top] = i;        for(int j = i; j <= 431; j += i) isp[j] = true;    }    for(int i = 2; i <= 431; i++)  {        int x = i;        for(int j = 1; j <= top; j++){            while (x % p[j] == 0) {                num[i][j]++;                x /= p[j];            }        }    }    for(int i = 2; i <= 431; i++) {        for(int j = 1; j <= top; j++) {            sum[i][j] += sum[i - 1][j] + num[i][j];        }    }}int main(){   // freopen("b.in", "r", stdin);    init();    while(~scanf("%d%d", &n, &m)) {        cal(n, m);        if (f[n][m]) {            cout << f[n][m] << endl;            continue;        }        long long  cur = 1LL;        for(int i = 1; i <= top; i++) cur *= (ans[i] + 1);        f[n][m] = cur;        f[n][n - m] = cur;        cout << cur << endl;    }    return 0;}

POJ 1696 Space Ant

[Solution]

每次向左偏转的角度尽可能地小,判断点是不是在左边通过叉积计算

[Code]

#include <cstdio>#include <cmath>#include <algorithm>using namespace std;#define N 2000const double eps = 1e-9;int n, top;bool f[N];struct Point {    double x, y;    int num;} a[N];int ans[N];bool cmp(Point a, Point b){    if (a.y == b.y)   return a.x < b.x;    return a.y < b.y;}int Cross(Point p0, Point p1, Point p2) {    double tot =  (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);    if (fabs(tot) < eps)  return 0;    return tot < 0 ? -1 : 1;}double dis(Point u, Point v){    return sqrt((u.x - v.x) * (u.x - v.x) + (u.y - v.y) * (u.y - v.y));}bool lcmp(Point u, Point v1, Point v2){    int d = Cross(u, v2, v1);    if (!d)  return dis(u, v2) < dis(u, v1);    return d > 0;}int main(){   //freopen("b.in", "r", stdin);    int T;    scanf("%d", &T);    while(T--)  {        scanf("%d", &n);        for(int i = 1; i <= n; i++) scanf("%d%lf%lf", &a[i].num, &a[i].x, &a[i].y);        sort(a + 1, a + n + 1, cmp);        for(int i = 1; i <= n; i++)  f[i] = false;        ans[1] = a[1].num;  f[1] = true;        Point lastu;        lastu.x = 0; lastu.y = a[1].y;        Point u = a[1];        int top = 1;        for(int i = 2; i <= n; i++) {            int minj = -1;            for(int j = 2; j <= n; j++) {                if (!f[j]  && (Cross(lastu, u, a[j]) >= 0))  {                    if (minj < 0 || lcmp(u, a[minj], a[j]))  minj = j;                }            }            if (minj < 0)  break;            ans[++top] = a[minj].num;            f[minj] = true;            lastu = u;            u = a[minj];        }        printf("%d", top);        for(int i = 1; i <= top; i++)  printf(" %d", ans[i]);        printf("\n");    }    return 0;}

POJ 1319 Pipe Fitters

[Solution]

对于第一种放置方式,直接是(int)a * (int)b
对于第二种放置方式,最后一行可以防止(int)b, 倒数第二层取决于b的小数部分
需要高度与实际高度的比例可以推出
注意第二种放置方式始终有一层的高度是无法优化的
注意第二种放置方式把a当行一次,把b当行一次

[Code]

#include<cstdio>#include<iostream>#include<cstring>#include<cmath>using namespace std;int cal(double a, double b){        int d1 = (int)b, d2 = (int) (b + 0.5);        d2--;        int k = (int)((a - 1) * 2 / sqrt(3.0)); // important        if (a >= 1) k++;        int ans2 = (k / 2) * (d1 + d2);        if (k % 2) ans2 += d1;        return ans2;}int main(){   // freopen("b.in", "r", stdin);    double a, b;    while (~scanf("%lf%lf", &a, &b))  {        int ans1 = (int)a * (int)b;        int ans2 = max(cal(a, b), cal(b, a));        if (ans1 >= ans2)            printf("%d grid\n", ans1);        else            printf("%d skew\n", ans2);    }    return 0;}

HDU 3746 Cyclic Nacklace

[Solution]

kmp判断

首先考虑虽然我们可以添加到左边,也可以添加到右边,但实际上等效于添加到一边,因为最终会连成一个环。

因此我们只思考在字符串的右面加上一些字符,考虑最后一位的next值,len-next[len]为循环节长度d, next[len] % d 便是最后多的那一部分

[Code]

#include<cstdio>#include<iostream>#include<cstring>using namespace std;#define N 200500char c[N];int nxt[N];void get_next(char* s, int* nxt){    int n = strlen(s);    int j = 0;    nxt[0] = nxt[1] = 0;    for(int i = 1; i < n; i++){        while (j > 0 && s[i] != s[j]) j = nxt[j];        if (s[i] == s[j]) j++;        nxt[i + 1] = j;    }}int main(){  //  freopen("b.in", "r", stdin);    int T;    scanf("%d", &T);    while (T--)  {        scanf("%s", c);        get_next(c, nxt);        int len = strlen(c);        int ans = len - nxt[len];        int t = nxt[len] % ans;        if (!t && nxt[len]) t = ans;        printf("%d\n", ans - t);    }    return 0;}

BZOJ 2186 沙拉公主的困惑

[Solution]

只想说尽可能不会再把数字转成字符串处理了
[Code]

#include<cstdio>#include<iostream>#include<cstring>using namespace std;#define N 3010#define M 2#define ll long longint cnt = 0;int id[N * 34], ch[N * 34][2], tim[N * 34];int pow[33];int n, m;int a[N];void init(){    cnt = 0;    for(int i = 0; i < M; i++) ch[0][i] = 0;    id[0] = 0;    tim[0] = -1;}void insert(int x, int k){    int u = 0;    for(int i = 30; i >= 0; i--)  {        int v = (x & (1 << i)) > 0 ? 1 : 0;        if (!ch[u][v]) {            ch[u][v] = ++cnt;            for(int j = 0; j < M; j++) ch[cnt][j] = 0;            id[cnt] = 0;            tim[cnt] = 0;        }        u = ch[u][v];        tim[u]++;    }    id[u] = k;}void update(int x, int t){    int u = 0;    for(int i = 30; i >= 0; i--)  {        int v = (x & (1 << i)) > 0 ? 1 : 0;        u = ch[u][v];        tim[u] += t;    }}int cal(int x){    int u = 0;    for(int i = 30; i >= 0; i--)  {        int v = (x & (1 << i)) > 0 ? 1 : 0;        if (ch[u][1 - v] && tim[ch[u][1 - v]]) u = ch[u][1 - v];        else u = ch[u][v];    }    return id[u];}int main(){   // freopen("b.in", "r", stdin);    int T;    scanf("%d", &T);    while(T--)  {        init();        scanf("%d", &n);        for(int i = 1; i <= n; i++)  {            scanf("%d", a + i);            insert(a[i], i);        }        int ans = 0;        for(int i = 1; i <= n; i++)  {            update(a[i], -1);            for(int j = i + 1; j <= n; j++) {                update(a[j], -1);                int k = cal(a[i] + a[j]);                ans = max(ans, ((a[i] + a[j]) ^ a[k]));                //printf("%d %d %d\n", i, j, k);                update(a[j], 1);            }            update(a[i], 1);        }        printf("%d\n", ans);    }    return 0;}
原创粉丝点击