HGOI8.13集训题解

题解

和杭二中搞了十几天..天天不会做..都是一群神仙在那里打架…我这个蒟蒻只能看看。今天和江苏常州打了一套比赛,觉得比较有难度。


第一题——指引(guide)

【题目描述】

  • 给出n个人和n个门的横纵坐标(x,y),一扇门只能通过一个人,每个人只能走到他右上方(即横纵坐标都大于等于人的坐标),求至多有多少人可以进门。题目给定x和y都互不相同且 0x,y2n 0 ≤ x , y ≤ 2 n

  • 真的拿到题就蒙逼了orz,没有看到已经给你离散化好了的条件,就想了一个小时怎么打暴力。
  • 贪心策略:一扇门尽量让离他进的人走
  • 因为0-2n之间的每个x和y都是存在的,无非是人和门的区别而已..那就直接把人塞入一个set,求出门最近的人的坐标,复杂度就是 O(2n) O ( 2 n )
#include <iostream>
#include <algorithm>
#include <set>
#include <cstdio>
#include <cstring>
using namespace std;
inline int read(){
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
void fff(){
    freopen("guide.in","r",stdin);
    freopen("guide.out","w",stdout);
}
const int N=1e6+10;
bool visited[N];
int pos[N];
int num;
int n;
set<int> st;
int main(){
//  fff();
    num=read();
    n=read();
    for (int i=1;i<=n;i++){
        int x,y;
        x=read(),y=read();
        visited[x]=true;
        pos[x]=y;
    }
    for (int i=1;i<=n;i++){
        int x,y;
        x=read(),y=read();
        visited[x]=false;
        pos[x]=y;
    }
    int ans=0;
    for (int i=0;i<2*n;i++){
        if(visited[i]) st.insert(pos[i]);
        else{
            set<int>::iterator tmp=st.lower_bound(pos[i]);
            if(tmp==st.begin()) continue;
            tmp--;ans++;
            st.erase(tmp);
        }
    }
    cout<<ans;
}

第二题——碎片(fragment)

【题目描述】

  • 给出n*m的 字符矩阵,每次操作能交换任意两行或者任意两列,求判断能否在有限次数内将给定矩阵变成中心对称的矩阵。

  • 一开始做这个题的时候想到了一些性质,但没有敢往下打,后来才发现光这些性质就有90分,有点亏。
  • 真的是没有想到这个是标准的dfs板子题。
  • 由模拟可知,每次操作,对于每一个字符的同一行和同一列的元素都是保持不变的,而且n和m的规模很小,那么可以通过前期预处理判断两行或者两列离散化之后是否有相互匹配。
  • 在进行dfs构建新生成的矩阵的时候,就可以利用上面这一条来进行可行性剪枝。
  • 每次dfs的时候不能进行图形的记录,而是选择进行反方向的映射,从而减少内存(如果每次dfs都要进行一次记录状态不是肯定会爆炸)。
  • 单方向进行搜索,反正只要两行对应,就肯定是等价的,而只要这两行能进行匹配,就一定是这两行匹配(如果另外也有能够匹配的,那么交换之后是等价的)。唯一的区别就是如果n或者m是奇数的时候中间一列和一行的选择会有所不同。
  • 然后水过。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
inline int read(){
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
void fff(){
    freopen("fragment.in","r",stdin);
    freopen("fragment.out","w",stdout);
}
int num,n,m;
char map[15][15];
char x[15],y[15];
bool row[15][15],col[15][15],solved;
int cnum[20],rnum[20];
bool viscol[20],visrow[20];
void check(){
    for (int i=1;i<=n;i++){
        for (int j=1;j<=n;j++){
            if(map[rnum[i]][cnum[j]]!=map[rnum[n-i+1]][cnum[m-j+1]]) return;
        }
    }
    solved=true;
    printf("YES\n");
}
void work2(int pos,int type){
    if(solved) return;
    if(pos==0){
        check();
        return;
    }
    if(type){
        for (int i=1;i<=m;i++){
            viscol[i]=true;
            cnum[pos]=i;
            work2(pos-1,0);
            viscol[i]=false;
        }
    }else{
        for (int i=1;i<=m;i++){
            if(viscol[i]) continue;
            for (int j=i+1;j<=m;j++){
                if(!viscol[j]&&col[i][j]){
                    viscol[i]=true;
                    viscol[j]=true;
                    cnum[pos]=i;
                    cnum[m+1-pos]=j;
                    work2(pos-1,0);
                    viscol[i]=false;
                    viscol[j]=false;
                }
            }
            return;
        }
    }
}
void work1(int pos,int type){
    if(solved) return;
    if(pos==0){
        work2((m+1)/2,m&1);
        return;
    }
    if(type){
        for (int i=1;i<=n;i++){
            visrow[i]=true;
            rnum[pos]=i;
            work1(pos-1,0);
            visrow[i]=false;
        }
    }else{
        for (int i=1;i<=n;i++){
            if(visrow[i]) continue;
            for (int j=i+1;j<=n;j++){
                if(!visrow[j]&&row[i][j]){
                    visrow[i]=true;
                    visrow[j]=true;
                    rnum[pos]=i;
                    rnum[n+1-pos]=j;
                    work1(pos-1,0);
                    visrow[i]=false;
                    visrow[j]=false;
                }
            }
            return;
        }
    }
}
int main(){
//  fff();
    num=read();
    int T=read();
    while (T--){
        n=read(),m=read();
        memset(visrow,0,sizeof(visrow));
        memset(viscol,0,sizeof(viscol));
        for (int i=1;i<=n;i++) scanf("%s",map[i]+1);
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++){
                for (int k=1;k<=m;k++){
                    x[k]=map[i][k];
                    y[k]=map[j][k];
                }
                sort(x+1,x+m+1);
                sort(y+1,y+m+1);
                row[i][j]=true;
                for (int k=1;k<=m;k++)
                    if(x[k]!=y[k]){
                        row[i][j]=false;
                    }
            }

        for (int i=1;i<=m;i++)
            for (int j=1;j<=m;j++){
                for (int k=1;k<=n;k++){
                    x[k]=map[k][i];
                    y[k]=map[k][j];
                }
                sort(x+1,x+n+1);
                sort(y+1,y+n+1);
                col[i][j]=true;
                for (int k=1;k<=n;k++)
                    if(x[k]!=y[k]){
                        col[i][j]=false;
                        break;
                    }
            }
        solved=false;
        work1((n+1)/2,n&1);
        if(!solved) printf("NO\n");
    }


}

第三题——寻梦(fantasy)

【题目描述】

  • 给定n和k,判断是否存在 p1|k+p2|k+p3|k+...pt|k=n p 1 | k + p 2 | k + p 3 | k + . . . p t | k = n ,同时保证 pi p i 不等于 n n
  • 有多组数据保证k的个数50

  • 看题干其实是一道图论题…但是建模之后就变成了一道数论题…(数学真是迷人)。
  • 看这个题目一脸懵逼,数据也都是很大。不知从何下手。

  • 考虑到n是由k的非1因数组成的,那么当n能够分解成k的若干个k的因数,则一定能够分解成k的质因数。那目的就很明确了嘛….

  • 有一个数据点k只有两个质因数,那直接拓展欧几里得…

  • k是有限个的,记忆化。
  • n写成有若干个k的质因数相加,令 p1 p 1 为k的最小质因数,那么n减去若干个 p1 p 1 之后所剩下的余数要由剩下的因数来组成,那就可以利用类似于dijkstra最短路的方法,求出被 p1 p 1 取模之后剩下的值的最小值,如果最终的结果小于n,则结果合法
#include<bits/stdc++.h>
using namespace std;
const int MAXQ = 55;
const int MAXLOG = 64;
const int MAXN = 100005;
const int MAXP = 2e6 + 5;
const int MAXV = 3.2e7 + 5;
const long long INF = 4e18;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
    x = 0; int f = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
    for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
    x *= f;
}
template <typename T> void write(T x) {
    if (x < 0) x = -x, putchar('-');
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
    write(x);
    puts("");
}
int q; long long memk[MAXQ];
int tot, prime[MAXP], f[MAXV];
int cnt[MAXQ]; long long p[MAXQ][MAXLOG];
long long dist[MAXQ][MAXN];
struct info {long long dist; int home; };
bool operator < (info a, info b) {
    return a.dist > b.dist;
}
void exgcd(long long a, long long b, long long &x, long long &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return;
    }
    long long q = a / b, r = a % b;
    exgcd(b, r, y, x);
    y -= q * x;
}
int main() {
//  freopen("fantasy.in", "r", stdin);
//  freopen("fantasy.out", "w", stdout);
    int num; read(num);
    for (int i = 2; i < MAXV; i++) {
        if (f[i] == 0) prime[++tot] = f[i] = i;
        for (int j = 1; j <= tot && prime[j] <= f[i]; j++) {
            int tmp = prime[j] * i;
            if (tmp >= MAXV) break;
            f[tmp] = prime[j];
        }
    }
    int T; read(T);
    while (T--) {
        long long n, k;
        read(n), read(k);
        int pos = 0;
        for (int i = 1; i <= q; i++)
            if (memk[i] == k) pos = i;
        if (pos == 0) {
            pos = ++q;
            memk[q] = k;
            long long tmp = k;
            for (int i = 1; 1ll * 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) {
                for (int i = 0; i < p[pos][1]; i++)
                    dist[pos][i] = INF;
                static priority_queue <info> Heap;
                dist[pos][0] = 0; Heap.push((info) {0, 0});
                static bool vis[MAXN];
                memset(vis, false, sizeof(vis));
                while (!Heap.empty()) {
                    while (!Heap.empty() && vis[Heap.top().home]) Heap.pop();
                    if (Heap.empty()) break;
                    info tmp = Heap.top(); Heap.pop();
                    for (int i = 2; i <= cnt[pos]; i++) {
                        int dest = (tmp.home + p[pos][i]) % p[pos][1];
                        if (dist[pos][dest] > tmp.dist + p[pos][i]) {
                            dist[pos][dest] = tmp.dist + p[pos][i];
                            Heap.push((info) {dist[pos][dest], dest});
                        }
                    }
                }
            }
        }
        bool flg = false;
        for (int i = 1; i <= cnt[pos]; i++)
            if (n % p[pos][i] == 0) {
                printf("YES\n");
                flg = true;
                break;
            }
        if (flg) continue;
        if (cnt[pos] <= 1) {
            printf("NO\n");
            continue;
        }
        if (cnt[pos] == 2) {
            long long x = 0, y = 0;
            exgcd(p[pos][1], p[pos][2], x, y);
            y = (y % p[pos][1] + p[pos][1]) % p[pos][1];
            long long tmp = y * (n % p[pos][1]) % p[pos][1] * p[pos][2];
            if (tmp <= n) printf("YES\n");
            else printf("NO\n");
            continue;
        }
        int tmp = n % p[pos][1];
        if (dist[pos][tmp] <= n) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值