游老师模拟赛5.27

1.Tower
【题目描述】
平面上有N个整点。如果将点(x0,y0)移动到(x1,y1),则需要的代价为|x0-x1|+|y0-y1|。求使得K(K=1…N)个点在同一位置上最少需要的代价。
【输入文件】
第一行1个正整数N ;
接下来N行,每行两个正整数xi和yi,为第i个点的坐标,不超过106。
【输出文件】
输出共N行,第i行为使得有i个点在同一位置的最少代价。
【样例输入】
4
15 14
15 16
14 15
16 15
【样例输出】
0
2
3
4
【数据规模】
对于100%的数据,满足1≤N≤50。

题解:

最优解必定是在(xi,yj)这些位置上的,因此只要枚举这些位置(共N2个),然后将所有点与这个位置的距离D[i]从小到大排序,然后S[i]=D[1]+D[2]+…+D[i]。那么Ans[k]=min{Ans[k],S[k]}。

标程:

#include
using namespace std;
int n,i,j,ans[52],x[52],y[52],k,d[52],sum;
int main(){
    scanf("%d",&n);
    for (i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]),ans[i]=1e9;
    for (i=1;i<=n;i++)
        for (j=1;j<=n;j++){
            for (k=1;k<=n;k++) d[k]=abs(x[i]-x[k])+abs(y[j]-y[k]);
            sort(d+1,d+n+1);
            sum=0;
            for (k=1;k<=n;k++) sum+=d[k],ans[k]=min(ans[k],sum);
        }
    for (i=1;i<=n;i++) printf("%d\n",ans[i]);
}

2.置换
题目描述:
n个数字,初始序列为1、2、3、4……n。现有m行n列的数字,每行为一个置换操作。如某行操作的第i个数字为a[i],那么就把原来序列中的第a[i]个数字放到现在这个序列的第i的位置上,然后组成新的序列。然后,看列表的第二行操作……、第三行操作……一直到最后一行操作,重复上面的操作。当最后一行的操作结束,组成了的序列又按照第一行来操作,然后第二行操作……第三行操作……一直循环下去,直到一共操作了k行为止。最后生成的这个序列就是我们最终的序列。
输入格式:
第一行三个数,n,m和k。
接下来m行,每行n个数。
输出格式:
一行,一共n个数,表示最终的礼品序列。n个数之间用一个空格隔开,行尾没有空格,需要回车。
输入样例:
7 5 8
6 1 3 7 5 2 4
3 2 4 5 6 7 1
7 1 3 4 5 2 6
5 6 7 3 1 2 4
2 7 3 4 6 1 5
输出样例:
2 4 6 3 5 1 7

数据范围:
1<=n<=100;1<=m<=10;1<=k<=2^31-1。
对于50%的数据,保证k<=500。这些数据每个数据点8分,其他的数据每个数据点12分。

题解:

置换是循环往复的,并且我们可以发现虽然K很大,但是m<=10,那么我们可以先预处理出a[i][j]表示第i次置换以后位置j上是原递增序列中的哪一个数,然后把k分成两部分:k/m 和k%m现在把m次置换称为一个大置换,那大置换的次数就是k/m次,剩下的k%m次是依次置换。由于我们一开始已经预处理出每次置换一个以后的序列,所以可以把a[m][1..n]和a[k%m][1..n]提取出来建立两个矩阵matrix1、matrix2,设初始的序列(即递增序列)ans,最终的序列就是ans*(matrix1^(k/m))*matrix2,注意顺序不能颠倒,笔者就被这个坑了好久。。。。matrix1的k/m次方用快速幂求。
时间复杂度:O(n^3logk)

标程:

#include
using namespace std;
int n,m,k,i,j,x,t;
struct ma{
    int a[103][103];
}A[12],B,C;
int read(){
    char c;int x=0,f=1;
    do{c=getchar();if(c=='-')f=-1;}while(c<48||c>57);
    do x=(x<<1)+(x<<3)+(c^48),c=getchar();while(c>=48&&c<=57);
    return f*x;
}
ma operator *(ma x,ma y){
    ma z;
    memset(z.a,0,sizeof(z.a));
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            for (int k=1;k<=n;k++) z.a[i][j]+=x.a[i][k]*y.a[k][j];
    return z;
}
ma operator ^(ma x,int y){
    ma z;
    memset(z.a,0,sizeof(z.a));
    for (int i=1;i<=n;i++) z.a[i][i]=1;
    for (;y;y>>=1,x=x*x)
        if (y&1) z=z*x;
    return z;
}
void print(ma A,int x,int y){
    for (int i=1;i<=x;i++)
        for (int j=1;j<=y;j++) printf("%d%c",A.a[i][j],j==y?'\n':' ');
}
int main(){
    n=read();m=read();k=read();
    for (i=1;i<=n;i++) C.a[i][i]=1;
    for (i=1;i<=m;i++)
        for (j=1;j<=n;j++){
            x=read();
            A[i].a[x][j]=1;
        }
    for (i=1;i<=n;i++) B.a[1][i]=i;
    for (i=1;i<=m;i++) C=C*A[i];
    B=B*(C^(k/m));
    for (i=1;i<=k%m;i++) B=B*A[i];
    print(B,1,n);
}

jxy大佬不用矩阵,用一个类似矩阵乘法的操作做到了O(nlogk),orz

#include
int n,m,k,tmp,i,j;
struct ma{
    int a[102];
}a[12],ans;
ma operator *(ma x,ma y){
    ma tmp=x;
    for (int i=1;i<=n;i++) x.a[i]=tmp.a[y.a[i]];
    return x;
}
void calc(int num){
    if (!num){
        ans=a[0];
        return;
    }
    calc(num>>1);
    ans=ans*ans;
    if (num&1) ans=ans*a[m];
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for (i=1;i<=n;i++) a[0].a[i]=i;
    for (i=1;i<=m;i++){
        for (j=1;j<=n;j++) scanf("%d",&a[i].a[j]);
        a[i]=a[i-1]*a[i];
    }
    calc(k/m);
    tmp=k%m;
    for (i=1;i<=n;i++) printf("%d ",ans.a[a[tmp].a[i]]);
}

3.数列
【题目描述】
给定一个长度是n的数列A,我们称一个数列是完美的,当且仅当对于其任意连续自序列的和都是正的。现在你有一个操作可以改变数列,选择一个区间[x,y]满足AX+AX+1+…+AY<0,1小于x<=y小于n,令S=AX+AX+1+…+AY,对于AX-1和AY+1分别加上S,AX和AY分别减去S(如果x = y就减两次).问最少几次这样的操作使得最终数列是完美的。
【输入文件】
第一行一个数n,以下n个数。
【输出文件】
一个数表示最少的操作次数,如果无解输出-1。
【样例输入】
5
13
-3
-4
-5
62
【样例输出】
2
【样例解释】
首先选择区间[2,4],之后数列变成1, 9, -4, 7, 50,然后选择[3,3],数列变成1, 5, 4, 3, 50
【数据规模】
对于20%的数据,满足1≤N≤5;
对于100%的数据,满足1≤N≤105;1≤ | A[i] | ≤ 231-1。

题解:

首先一个满足条件的序列就是一个前缀和严格递增的数列,
然后我们可以发现一个重要的性质:操作[x,y]就是交换x-1和y的前缀和!于是问题就简单了:用最少的交换次数将前缀和数列排序。这个问题可以通过简单的找环解决。首先对问题进行离散化,然后看当前的置换中有几个圈,如果有K个,那么答案就是N-K。注意判断无解的情况。

标程:

#include
using namespace std;
typedef long long ll;
const int N=100003;
struct kk{
    ll sum;
    int id;
}a[N];
int i,x,ne[N],t,n,ans;
int read(){
    char c;int x=0,f=1;
    do{c=getchar();if(c=='-')f=-1;}while(c<48||c>57);
    do x=(x<<1)+(x<<3)+(c^48),c=getchar();while(c>=48&&c<=57);
    return f*x;
}
bool cmp(kk x,kk y){
    return x.sumint main(){
    n=read();
    for (i=1;i<=n;i++){
        x=read();
        if (x==0){
            printf("-1");
            return 0;
        }
        a[i].sum=a[i-1].sum+x;
        a[i].id=i;
    }
    if (a[n].sum<=0){
        printf("-1");
        return 0;
    }
    sort(a+1,a+n,cmp);
    for (i=1;ifor (t=a[i].id;ne[t];t=ne[t]);
        if (t!=i) ans++,ne[i]=t;
    }
    printf("%d",ans);
}

你可能感兴趣的:(模拟赛,游老师)