• 计蒜客NOIP2018模拟1


    https://www.jisuanke.com/contest/1152

    T1:最失败的一道题,其实就是道水题,好几种写法,一种都没想出来。

    题意转化后就是:每个数可以选a[i]和a[i]-k,最后求使1,2,3,...,T都存在的最大的T+1和最多能让多少个数小于等于T。

    为什么第一问可以转化成求有多少个数小于等于T呢?首先不大于k的怪物可以直接杀死,然后大于k的怪物显然当且仅当血量小于等于T时才可能被用第二种操作杀死,所以当T最大一定是第二问最优的情况。所以第二问做完后直接扫一边就是第一问的答案。

    考虑第二问怎么求:

    方法一:二分答案。二分T判断可行性即可,没有任何坑点。$O(nlog n)$

    方法二:直接循环。c[]记录每个数有多少个。从1枚举,若 c[i]>0 则 c[i]-- 否则 c[i+k]--,某个数小于0了就终止循环。$O(n)$

    方法三:若a[i]>k则连无向边<a[i],a[i]-k>否则连自环<a[i],a[i]>。考虑这个图的每个连通块,如果边数等于点数-1(也就是一棵树),则必然有一个数取不到(显然这个数要选最大的那个),否则块内所有数都能取到。并查集维护连通块即可。$O(nalpha(n))$

    方法四:继续方法三的思想,每个块dfs一遍就可以求出块内的点数和边数了。$O(n)$

    前两种简直是无脑方法我竟然都没想到,后面两种应该是一个常用套路,以后发现数的范围也是$O(n)$级别的话就要从数的角度考虑了(比如说建图或生成函数做FFT)

    方法一:

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define rep(i,l,r) for (int i=l; i<=r; i++)
     4 using namespace std;
     5 
     6 const int N=100100;
     7 int n,k,a[N],ans;
     8 bool b[N];
     9 
    10 int jud(int mid){
    11     rep(i,1,mid+1) b[i]=0;
    12     rep(i,1,n){
    13         if (a[i]<=k) { b[a[i]]=1; continue; }
    14         if (a[i]>mid) { b[a[i]-k]=1; continue; }
    15         if (!b[a[i]-k]) b[a[i]-k]=1; else b[a[i]]=1;
    16     }
    17     rep(i,1,mid) if (!b[i]) return 0;
    18     return 1;
    19 }
    20 
    21 int main(){
    22     freopen("A.in","r",stdin);
    23     freopen("A.out","w",stdout);
    24     scanf("%d%d",&n,&k);
    25     rep(i,1,n) scanf("%d",&a[i]);
    26     sort(a+1,a+n+1);
    27     if (!jud(1)){
    28         int s=0;
    29         for (; s<n && a[s+1]<=k; s++);
    30         printf("%d %d
    ",s,1);
    31         return 0;
    32     }
    33     int L=1,R=a[n]; ans=0;
    34     while (L<=R){
    35         int mid=(L+R)>>1;
    36         if (jud(mid)) ans=mid,L=mid+1; else R=mid-1;
    37     }
    38     int s=0;
    39     for (; s<n && (a[s+1]<=k || a[s+1]-k<=ans); s++);
    40     printf("%d %d
    ",s,min(n,ans+1));
    41     return 0;
    42 }

    方法二:

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define rep(i,l,r) for (int i=l; i<=r; i++)
     4 using namespace std;
     5 
     6 const int N=100100;
     7 int n,k,ans,a[N],c[N];
     8 
     9 int main(){
    10     freopen("A.in","r",stdin);
    11     freopen("A.out","w",stdout);
    12     scanf("%d%d",&n,&k); int s=1;
    13     rep(i,1,n) scanf("%d",&a[i]),c[a[i]]++;
    14     while (1){
    15         if (c[s]) c[s]--;
    16         else if (c[s+k]) c[s+k]--;
    17             else break;
    18         s++;
    19     }
    20     rep(i,1,n) if (a[i]<=k || a[i]-k<=s-1) ans++;
    21     printf("%d %d
    ",ans,min(n,s));
    22     return 0;
    23 }

    方法四:

     1 #include<cstdio>
     2 #include<vector>
     3 #include<algorithm>
     4 #define rep(i,l,r) for (int i=l; i<=r; i++)
     5 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
     6 using namespace std;
     7 
     8 const int N=200100;
     9 int n,k,ans,mx,cnt,tot,b[N],vis[N],bel[N],a[N],pe[N],pv[N],to[N],nxt[N],h[N];
    10 vector<int>V[N];
    11 
    12 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
    13 
    14 void dfs(int x,int fa,int co){
    15     V[co].push_back(x); bel[x]=co; vis[x]=1; pv[co]++;
    16     For(i,x) if ((k=to[i])!=fa && k!=x && !vis[k]) dfs(k,x,co);
    17 }
    18 
    19 int main(){
    20     freopen("A.in","r",stdin);
    21     freopen("A.out","w",stdout);
    22     scanf("%d%d",&n,&k); int s=1;
    23     rep(i,1,n) scanf("%d",&a[i]),mx=max(mx,a[i]);
    24     rep(i,1,n) if (a[i]>k) add(a[i],a[i]-k),add(a[i]-k,a[i]); else add(a[i],a[i]),add(a[i],a[i]);
    25     rep(i,1,mx) if (!vis[i]) tot++,dfs(i,0,tot);
    26     rep(i,1,tot) sort(V[i].begin(),V[i].end());
    27     for (int i=1; i<=cnt; i+=2) pe[bel[to[i]]]++;
    28     rep(i,1,tot) if (pe[i]<pv[i]) b[V[i][V[i].size()-1]]=1;
    29     for (; !b[s]; s++);
    30     rep(i,1,n) if (a[i]<=k || a[i]-k<=s-1) ans++;
    31     printf("%d %d
    ",ans,min(min(mx+1,n),s));
    32     return 0;
    33 }

    T2:

    首先参考[JSOI2017]原力,这是一个套路,但是只有80分。

    考虑一种做法:先求出图的一棵生成树。假设三角形有至少一条边在树上,那么枚举一条非树边<u,v>,直接看fa[u]是否和v相邻即可。如果没有满足条件的边则说明这棵树上没有边在三角形上,删去所有树边。重复以上操作即可,复杂度$O(frac{m^2}{n})$。

    这个做法当n在4000左右时显然会很大,但这个时候直接bitset搞一下就可以了,复杂度$O(mn/64)$。

    综合以上两种方法即可拿到满分。

    80分:

    #include<map>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define rep(i,l,r) for (int i=l; i<=r; i++)
    typedef long long ll;
    using namespace std;
    
    const int N=1000100,p=100003,p1=1000003;
    int n,m,u,v,cnt,tot,d[N],id[N],to[N<<1],nxt[N<<1],h[N],hash1[p1+100],hash2[p1+100],S[210][210];
    void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
    void Hash(int u,int v){ int i=(u*p+v)%p1; for (; hash1[i]; i=(i+1)%p1); if (!hash1[i]) hash1[i]=u,hash2[i]=v; }
    int find(int u,int v){
        int i=(u*p+v)%p1;
        for (; hash1[i] && (hash1[i]!=u || hash2[i]!=v); i=(i+1)%p1);
        if (!hash1[i]) return 0; else return i;
    }
    
    int main(){
        freopen("B.in","r",stdin);
        freopen("B.out","w",stdout);
        scanf("%d%d",&n,&m); int si=(int)sqrt(n);
        if (n<=200){
            rep(i,1,m) scanf("%d%d",&u,&v),S[u][v]=1,S[v][u]=1;
            rep(i,1,n-2) rep(j,i+1,n-1) if (S[i][j]) rep(k,j+1,n) if (S[j][k] && S[i][k])
                { printf("%d %d %d
    ",i,j,k); return 0; }
        }
        rep(i,1,m) scanf("%d%d",&u,&v),add(u,v),add(v,u),d[u]++,d[v]++,Hash(min(u,v),max(u,v));
        rep(i,1,n) if (d[i]>=si) id[++tot]=i;
        rep(i,1,tot-2) rep(j,i+1,tot-1) rep(k,j+1,tot)
            if (find(id[i],id[j]) && find(id[j],id[k]) && find(id[i],id[k])){
                printf("%d %d %d
    ",id[i],id[j],id[k]); return 0;
            }
        rep(i,1,n) if (d[i]<si){
            for (int j=h[i]; j; j=nxt[j])
                for (int k=nxt[j]; k; k=nxt[k]) if (find(to[j],to[k]) || find(to[k],to[j])){
                    int x=i,y=to[j],z=to[k];
                    if (x>y) swap(x,y);
                    if (y>z) swap(y,z);
                    if (x>y) swap(x,y);
                    printf("%d %d %d
    ",x,y,z); return 0;
                }
        }
        return 0;
    }

    方法二(bitset):

    #include<cstdio>
    #include<bitset>
    #include<algorithm>
    #define rep(i,l,r) for (int i=l; i<=r; i++)
    using namespace std;
    
    const int N=100010;
    int n,m,u,v,U[N],V[N];
    bitset<4010>a[4010];
    
    int main(){
        freopen("B.in","r",stdin);
        freopen("B.out","w",stdout);
        scanf("%d%d",&n,&m);
        rep(i,1,m) scanf("%d%d",&u,&v),a[u].set(v),a[v].set(u),U[i]=u,V[i]=v;
        if (n<=4000){
            rep(i,1,m){
                bitset<4010>C=a[U[i]]&a[V[i]];
                if (C.none()) continue;
                int s1=U[i],s2=V[i];
                rep(j,1,n) if (C[j]){
                    int s3=j;
                    if (s1>s2) swap(s1,s2);
                    if (s2>s3) swap(s2,s3);
                    if (s1>s2) swap(s1,s2);
                    printf("%d %d %d
    ",s1,s2,s3);
                    return 0;
                }
            }
        }
        return 0;
    }

    满分:

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
     4 typedef long long ll;
     5 using namespace std;
     6 
     7 const int N=100100,M=4200100;
     8 int n,m,tot,a[5000][200],h[M],x[M],y[M],h1[M],nxt1[M],fa[N],nxt[N],used[N],que[N];
     9 
    10 int add(int a, int b){
    11     x[++tot] = a; y[tot] = b; h[tot] = nxt[a]; nxt[a] = tot;
    12     int h = (a * 30000 + b) % 999983; nxt1[tot] = h1[h]; h1[h] = tot;
    13     return 0;
    14 }
    15 
    16 int findh(int a, int b){
    17     int h = (a * 30000 + b) % 999983;
    18     int i = h1[h];
    19     while (i != 0) {
    20         if ((a == x[i]) && (b == y[i]))  return 1;
    21         else  i=nxt1[i];
    22     }
    23     return 0;
    24 }
    25 
    26 void fin(int i, int j, int k){
    27     if (j > k) swap(j,k);
    28     if (i > j) swap(i,j);
    29     if (j > k) swap(j,k);
    30     printf("%d %d %d
    ", i, j, k);
    31 }
    32 
    33 int alg1(){
    34     for (int i = 1; i <= m; i++) {
    35         for (int j = 0; j <= n / 30; j++)
    36             if ((a[x[2 * i - 1]][j] & a[y[2 * i - 1]][j]) != 0) {
    37                 int s = a[x[2 * i - 1]][j] & a[y[2 * i - 1]][j];
    38                 int k = 0;
    39                 while (s > 0) s = s / 2,k++;
    40                 fin(x[2 * i - 1], y[2 * i - 1], j * 30 + k - 1);
    41                 return 0;
    42             }
    43     }
    44     return 0;
    45 }
    46 
    47 int alg2(){
    48     int round = 0;
    49     while (1 != 0) {
    50         round++; int p = 1,q = 0;
    51         for (int i = 1; i <= n; i++) {
    52             if (used[i] != round) que[++q] = i,fa[i] = i;
    53             while (p <= q) {
    54                 int ptr = nxt[que[p]];
    55                 while (ptr != 0) {
    56                     if (used[y[ptr]] != round) {
    57                         que[++q] = y[ptr];
    58                         used[y[ptr]] = round;
    59                         fa[y[ptr]] = que[p];
    60                     }
    61                     ptr = h[ptr];
    62                 }
    63                 p++;
    64             }
    65         }
    66         for (int i = 1; i <= tot; i++)
    67             if ((x[i] != fa[x[i]]) && (y[i] != fa[x[i]]) && (findh(y[i], fa[x[i]]) == 1)) {
    68                 fin(x[i], fa[x[i]], y[i]); return 0;
    69             }
    70     }
    71     return 0;
    72 }
    73 
    74 int main(){
    75     freopen("B.in","r",stdin);
    76     freopen("B.out","w",stdout);
    77     scanf("%d%d", &n, &m); tot = 0;
    78     for (int i = 1; i <= m; i++) {
    79         int p, q;
    80         scanf("%d%d", &p, &q);
    81         add(p, q); add(q, p);
    82         if (n <= 5000) {
    83             int c = q / 30,d = q % 30;
    84             a[p][c] += (1 << d);
    85             c=p/30,d=p%30;
    86             a[q][c] += (1<<d);
    87         }
    88     }
    89     if (n <= 5000)  alg1(); else  alg2();
    90     return 0;
    91 }

    T3:考虑只有第一种概率模型的情况,从角的角度考虑。设同色三角形个数为$a$,异色为$b$,同色角个数x,异色$y$。则有$x=3a+b,y=2b$,解得$a=frac{2x-y}{6}$

  • 相关阅读:
    架构设计
    git 常用命令
    C# 加载C++的dll
    windows 服务部署管理
    wpf 模板绑定控件属性
    golang开启module模式 go mod
    使用docker安装redis
    使用docker安装elasticsearch
    使用docker安装etcd
    使用docker安装mysql5.7
  • 原文地址:https://www.cnblogs.com/HocRiser/p/8687730.html
Copyright © 2020-2023  润新知