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;
}
原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13753304.html