zoukankan      html  css  js  c++  java
  • 9月30日考试 题解(贪心+搜索+同余)

    100+65+21=186.同余没想出来太可惜了,本来能上200的QAQ

    --------------------

    T1 指引

    题目大意:给定$n$个人的坐标和$n$个门的坐标,每个人只能增大自己的横纵坐标,每个门只能让一个人进。问最多能有几个人走进门。$nleq 1e5$

    贪心。显然如果一个人$(x_1,y_1)$能走到门$(x_2,y_2)$,那么肯定有$x_1 leq x_2 ,y_1 leq y_2$。先按照$x$排序,然后考虑每个门所作出的贡献:把横坐标合法的人的纵坐标用数据结构维护一下,看有没有人纵坐标也合法。我用的STL::set。其他数据结构诸如线段树或树状数组也可以。

    代码:

    #include<cstdio>
    #include<iostream>
    #include<set>
    #include<algorithm>
    using namespace std;
    const int N=100005;
    int n,num,l=1,cnt,now=1,ans;
    struct node
    {
        int x,y,id;
    }a[N],b[N],c[N],d[N],ex[N];
    set<int> s;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    bool cmp(node x,node y)
    {
        if (x.x==y.x) return x.y<y.y;
        return x.x<y.x; 
    }
    int main()
    { 
        num=read();n=read();
        for (int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(),a[i].id=i;
        for (int i=1;i<=n;i++) b[i].x=read(),b[i].y=read();
        for (int i=1;i<=n;i++) c[i]=a[i],d[i]=b[i];
        sort(c+1,c+n+1,cmp);
        sort(d+1,d+n+1,cmp);
        while(d[l].x<c[1].x) l++;
        for (int i=l;i<=n;i++) ex[++cnt]=d[i];
        sort(ex+1,ex+cnt+1,cmp);
        sort(a+1,a+n+1,cmp);
        for (int i=1;i<=cnt;i++)
        {
            while(now<=n&&ex[i].x>=a[now].x) s.insert(a[now].y),now++;
            set<int>::iterator tmp=s.lower_bound(ex[i].y);
            if (tmp!=s.begin()) tmp--,ans++,s.erase(tmp);
        }
        printf("%d",ans);
        return 0;
    }

    T2 碎片

    题目大意:给定一个$n*m$的字符图案,多组数据,问是否能通过行和列的变换使得图案变成一个中心对称图形。$n,mleq 12$。

    爆搜+特判理论上能有70pts.正解实质上就是优化的爆搜。

    首先有如下观察:

    1.每一行/列的可重集合不变。

    2.题目要求使得我们可以任意交换行/列的顺序。

    3.对于能够中心对称的两个行/列A和B,它们的集合相同。

    4.如果存在中心对称图形,那么任意交换两个对称的行/列,图形仍然是中心对称图形。

    有如上性质,我们可以考虑搜索。先枚举行/列,若行/列为奇数,那么先固定中间的一行/列,然后每次枚举一对对称的行/列。单次枚举的复杂度M上限11*9*7*5*3*1=10395。时间复杂度为$O(nm10395^2)$。

    但实际上我们可以预处理出每两行/两列的集合是否相同,然后利用观察3进行剪枝。实际上的复杂度远远达不到上限,甚至跑的飞起(最慢也只用了2ms)。好多人各种玄学做法也取得了不错的分数:随机化、同构、爆搜等等。怕不是出题人用脚造数据……

    代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=15;
    char mp[N][N];
    int num,T,n,m,r[N],l[N],flag;
    bool row[N][N],line[N][N],visr[N],visl[N];
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void check()
    {
        for (int i=1;i<=n;i++)
            for (int j=1;j<=m;j++)
                if (mp[r[i]][l[j]]!=mp[r[n-i+1]][l[m-j+1]]) return;
        printf("YES
    ");
        flag=1;
    }
    inline void solvel(int pos,int type)
    {
        if (flag) return;
        if (pos==0)
        {
            check();
            return;
        }
        if (type)
        {
            for (int i=1;i<=m;i++)
            {
                visl[i]=1;
                l[pos]=i;
                solvel(pos-1,0);
                visl[i]=0;
            }
        }
        else
        {
            for (int i=1;i<=m;i++)
            {
                if (visl[i]) continue;
                for (int j=i+1;j<=m;j++)
                    if (!visl[j]&&line[i][j])
                    {
                        visl[i]=1;visl[j]=1;
                        l[pos]=i;l[m-pos+1]=j;
                        solvel(pos-1,0);
                        visl[i]=0;visl[j]=0;
                    }
                return;
            }
        }
    }
    inline void solver(int pos,int type)
    {
        if (flag) return;
        if (pos==0)
        {
            solvel((m+1)/2,m&1);
            return;
        }
        if (type)
        {
            for (int i=1;i<=n;i++)
            {
                visr[i]=1;
                r[pos]=i;
                solver(pos-1,0);
                visr[i]=0;
            }
        }
        else
        {
            for (int i=1;i<=n;i++)
            {
                if (visr[i]) continue;
                for (int j=i+1;j<=n;j++)
                    if (!visr[j]&&row[i][j])
                    {
                        visr[i]=1;visr[j]=1;
                        r[pos]=i;r[n-pos+1]=j;
                        solver(pos-1,0);
                        visr[i]=0;visr[j]=0;
                    }
                return;
            }
        }
    }
    int main()
    {
        num=read();T=read();
        while(T--)
        {
            n=read(),m=read();
            for (int i=1;i<=n;i++)
                scanf("%s",mp[i]+1);
            for (int i=1;i<=n;i++)
                for (int j=1;j<=n;j++)
                {
                    static char x[N],y[N];
                    for (int k=1;k<=m;k++)
                        x[k]=mp[i][k],
                        y[k]=mp[j][k];
                    sort(x+1,x+m+1);
                    sort(y+1,y+m+1);
                    row[i][j]=1;
                    for (int k=1;k<=m;k++)
                        if(x[k]!=y[k]) row[i][j]=0;
                }
                for (int i=1;i<=m;i++)
                    for (int j=1;j<=m;j++)
                    {
                        static char x[N],y[N];
                        for (int k=1;k<=n;k++)
                            x[k]=mp[k][i],
                            y[k]=mp[k][j];
                        sort(x+1,x+n+1);
                        sort(y+1,y+n+1);
                        line[i][j]=1;
                        for (int k=1;k<=n;k++)
                            if (x[k]!=y[k]) line[i][j]=0;
                    }
            flag=0;
            solver((n+1)/2,n&1);
            if (!flag) printf("NO
    ");
        }
        return 0;
    }

    T3 寻梦

    题目大意:给定$n$和$k$。求出一组解使得$forall x  in [1,n]$,有$x_i | k$且$sum x_i=n$。$nleq 10^{18},kleq 10^{15}$。不同的$k$的个数不超过50。多组数据。

    易得知正整数都可以用质数表示出来,所以我们只需考虑$k$的质因数。所以问题变成了求解形如$sum a_ix_i=n$的式子。对于不同的$k$,我们分情况讨论:

    1.$k$只有1个质因数。我们直接判断$n$能否整除于它即可。

    2.$k$有两个质因数。这时问题转化为求解$ax+by=n$。扩展欧几里得处理。

    3.$k$有多个质因数。同余最短路处理。选取一个最小的质因数$p$作为$base$,设$f[i]$表示用其他质因数表示的模$p$为$i$的最小的数。有转移形如:$f[i+x]=f[i]+x$。考虑到与最短路中的松弛操作相似,于是可以利用最短路求解。判断是否有解则可以看$f[n mod p]$是否小于等于$n$。考虑到若有解,则$f[n mod p]$与$n$在模$p$意义下同余,若$f[n mod p] leq n$,那么$f[n mod p]$可以通过加若干次$p$使得其等于$n$。

    时间复杂度$O(50sqrt k + Tplog k)$。

    代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define int long long
    using namespace std;
    const int N=1000005;
    const int V=3.2e7+5;
    const int Q=55;
    const int inf=1e18;
    int memk[Q],prime[N*2],vis[V],dis[Q][N],cnt[Q],p[Q][N];
    int num,T,n,k,tot,q;
    struct node
    {
        int dis,pos;
    };
    bool operator < (node a,node b)
    {
        return a.dis>b.dis;
    }
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void exgcd(int a,int b,int &x,int &y)
    {
        if (!b)
        {
            x=1;y=0;
        }
        else
        {
            exgcd(b,a%b,x,y);
            int t=x;x=y;y=t-a/b*y;
        }
    }
    inline void solve()
    {
        for (int i=2;i<V;i++)
        {
            if (!vis[i]) prime[++tot]=vis[i]=i;
            for (int j=1;j<=tot&&prime[j]<=vis[i];j++)
            {
                int tmp=prime[j]*i;
                if (tmp>=V) break;
                vis[tmp]=prime[j];
            }
        }
    }
    inline void spfa(int pos)
    {
        priority_queue<node> q;
        for (int i=0;i<p[pos][1];i++)
            dis[pos][i]=inf;
        dis[pos][0]=0;q.push((node){0,0});
        while(!q.empty())
        {
            node now=q.top();q.pop();
            for (int i=2;i<=cnt[pos];i++)
            {
                int dist=(now.pos+p[pos][i])%p[pos][1];
                if (dis[pos][dist]>now.dis+p[pos][i])
                {
                    dis[pos][dist]=now.dis+p[pos][i];
                    q.push((node){dis[pos][dist],dist});
                }
            }
        }
    }
    signed main()
    {
        solve();
        num=read(),T=read();
        while(T--)
        {
            n=read();k=read();
            int pos=0;
            for (int i=1;i<=q;i++)
                if (memk[i]==k) pos=i;
            if (pos==0)
            {
                pos=++q;memk[q]=k;
                int tmp=k;
                for (int i=1;prime[i]*prime[i]<=tmp;i++)
                {
                    if (tmp%prime[i]==0)
                    {
                        p[pos][++cnt[pos]]=prime[i];
                        while(tmp%prime[i]==0) tmp/=prime[i];
                    }
                }
                if (tmp!=1) p[pos][++cnt[pos]]=tmp;
                if (cnt[pos]>=3) spfa(pos);
            }
            bool flag=0;
            for (int i=1;i<=cnt[pos];i++)
                if (n%p[pos][i]==0)
                {
                    printf("YES
    ");
                    flag=1;
                    break;
                }
            if (flag) continue;
            if (cnt[pos]<=1){
                printf("NO
    ");
                continue;
            }
            if (cnt[pos]<=2)
            {
                int x=0,y=0;
                exgcd(p[pos][1],p[pos][2],x,y);
                y=(y%p[pos][1]+p[pos][1])%p[pos][1];
                int tmp=y*(n%p[pos][1])%p[pos][1]*p[pos][2];
                if (tmp<=n) printf("YES
    ");
                else printf("NO
    ");
                continue;
            }
            int tmp=n%p[pos][1];
            if (dis[pos][tmp]<=n) printf("YES
    ");
            else printf("NO
    ");
        }
        return 0;
    }
  • 相关阅读:
    iOS-实现键盘右上角完成按钮
    iOS-开发中单例模式的实现
    iOS-实现高斯模糊效果(swift)
    iOS-解决UITableView有footerView时最后一个cell不显示分割线问题
    fenics 笔记 -- Possion Problem
    笔记
    Hyper-reduced projective dynamics 手推公式
    Gmsh 四面体单元剖分
    SoftRoboSim 之程序框架
    物理引擎中的时间积分方法及求解
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13753304.html
Copyright © 2011-2022 走看看