HGOI7.16集训题解

题解

今天做的是14年noipday2的真题。但真的比较一般..(首测230..被小坑坑到了)。两天加起来大概500不到吧..


第一题——无线网路发射器选址(wireless)

【题目描述】

  • 告诉你在坐标轴上有若干给点有不同的权值。求出在某个点建立的最大覆盖面积为(2d*2d)的使得覆盖权值之和最大。并求出最大的方案数。

  • 这道题是真的水..数据真的小…..手打暴力AC..orz。
  • 不怎么想在简单题上浪费时间。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
void fff(){
    freopen("wireless.in","r",stdin);
    freopen("wireless.out","w",stdout);
}
int n,d;
int mp[130][130];
int sum[130][130];
int main(){
    fff();
    memset(mp,0,sizeof(mp));
    memset(sum,0,sizeof(mp));
    cin>>d>>n;
    for (int i=1;i<=n;i++){
        int x,y,k;
        scanf("%d%d%d",&x,&y,&k);
        mp[x][y]=k;
    }
    int maxx=0,cnt=0;
    for (int i=0;i<=128;i++){
        for (int j=0;j<=128;j++){
            int l1=min(i+d,128),l2=max(i-d,0);
            int r1=min(j+d,128),r2=max(j-d,0);
            int temp=0;
            for (int ti=l2;ti<=l1;ti++){
                for (int tj=r2;tj<=r1;tj++){
                    temp+=mp[ti][tj];
                }
            }
            if(temp>maxx){
                maxx=temp;
                cnt=1;
            }else if(maxx==temp){
                cnt++;
            }
        }
    }
    printf("%d %d",cnt,maxx);
}

第二题——寻找道路(road)

【题目描述】

  • 求有向图的最短路并使得路径上的非初终节点的子节点能间接达到终点。

  • 其实这道题第一眼看上去没什么思路,但有种算法叫做正难则反。
  • 要保证路径上的点的子节点能直接或间接到达终点,那我们反过来先把不能达到的点求出来,并将它们的正向父节点标记掉。
  • 再来做spfa得出答案。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define INF 0x3f3f
using namespace std;
void fff(){
    freopen("road.in","r",stdin);
    freopen("road.out","w",stdout);
}
const int MAXN=10086;
int n,m,s,t;
struct Edge{
    int from,to;
};
vector <Edge> edge1,edge2;
vector <int> G1[MAXN],G2[MAXN];
bool visited[MAXN];
int cc[MAXN];
void dfs1(int u,int fa){
    int siz=G2[u].size();
    for (int i=0;i<siz;i++){
        Edge &e=edge2[G2[u][i]];
        if(!cc[e.to]){
            cc[e.to]=1;
            dfs1(e.to,u);
        }
    }
}
int dist[MAXN];
queue <int> q;
void spfa(){
    memset(dist,INF,sizeof(dist));
    visited[s]=true;
    q.push(s);
    dist[s]=0;
    for (int i=1;i<=n;i++){
        if(cc[i]==0){
            int siz=G2[i].size();
            for (int j=0;j<siz;j++){
                int v=edge2[G2[i][j]].to;
                int t;
                if(cc[v]==1)cc[v]=2;
            }
        }
    }
    while(!q.empty()){
        int tt=q.front();q.pop();
        visited[tt]=true;
        bool flag=false;
        int siz=G1[tt].size();

        for (int i=0;i<siz;i++){
            Edge &e=edge1[G1[tt][i]];
            if(cc[e.to]==1&&dist[tt]+1<dist[e.to]){
                dist[e.to]=dist[tt]+1;
                if(!visited[e.to]){
                    q.push(e.to);
                    visited[e.to]=true;
                }
            }
        }
    }
    if(dist[t]<INF){
        printf("%d",dist[t]);
    }else{
        printf("-1");
    }
}
int main(){
    fff();
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        edge1.push_back((Edge){x,y});
        G1[x].push_back(edge1.size()-1);
        edge2.push_back((Edge){y,x});
        G2[y].push_back(edge2.size()-1);
    }
    scanf("%d%d",&s,&t);
    memset(cc,0,sizeof(cc));
    cc[t]=1;
    dfs1(t,0);
    spfa();
    return 0;
}

第三题——解方程(equation)

【题目描述】

  • 给出n次方程,求出范围在 [1,m] [ 1 , m ] 内的所有整数解。系数 a a 的范围在|ai|1010000

  • 本来以为只能那个30分的(我真的是个蒟蒻…),但没有想到自己的算法能够拿到70分。
  • 首先这么大的系数,肯定需要进行高精的预处理。但总不可能带着10000位数来进行运算吧。所以需要运用到类hash的算法。将这个数取模。再来利用秦九昭算法来算出答案判断是否是正解。

  • 但是正解要稍微复杂一些。取模的个数不只是1个,而是采用多个。(防止报错),再利用积性函数的性质,对于当前的不合法的情况,则接下来的相同模数的结果也是不合法的(由于取模是质数的话会产生周期性)。再来筛去大部分的不合法的情况。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define LL long long
using namespace std;
void fff(){
    freopen("equation.in","r",stdin);
    freopen("equation.out","w",stdout);
}
const int MAXN=110;
const int MAXM=10000000;
const int cntmod=6;
int MOD[7]={233,12007,10007,666666,3103,65537};
int n,m;
int ans[10000],cnt=0;
LL a[MAXM][10];
bool flag[MAXM];
int main(){
//  fff();
    scanf("%d%d",&n,&m);
    for (int i=0;i<=n;i++){
        char c=getchar();
        while (!isdigit(c)&&c!='-') c=getchar();
        bool fl=(c=='-');
        for (int j=0;j<cntmod;j++){
            a[i][j]=(c=='-'?0:c-'0');
        }
        c=getchar();
        while (isdigit(c)){
            for (int j=0;j<cntmod;j++){
                a[i][j]=(a[i][j]*10+c-'0')%MOD[j];
            }
            c=getchar();
        }
        if(fl){
            for (int j=0;j<cntmod;j++)
                a[i][j]*=-1;
        }
    }
    for (int i=0;i<=m;i++){
        flag[i]=1;
    }
    for (int i=0;i<=m;i++){
        if(flag[i]){
            bool rt=1;
            for (int j=0;rt&&j<cntmod;j++){
                LL val=a[0][j],x=i%MOD[j];
                for (int k=1;k<=n;k++){
                    val=(val+a[k][j]*x%MOD[j])%MOD[j];
                    x=i*x%MOD[j];
                }
                if(val){
                    rt=false;
                    for (int k=i;k<=m;k+=MOD[j]){
                        flag[k]=false;
                    }
                }
            }
            if(rt) ans[++cnt]=i;
        }
    }
    printf("%d\n",cnt);
    for (int i=1;i<=cnt;i++){
        printf("%d\n",ans[i]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值