[NOIP2009]潜伏者,Hankson's Problem,最优贸易,靶形数独

这一年的题感到有些奇怪,跟做前后两年的题感觉都不一样,还是有一点难度的。虽然据计算骗分也能达到一等奖的分数,但就是没骗到啊。那年全省没有满分的,但现在时代变了,能得满分的人估计也很多,还是提高打模板的能力,不然知道怎么做都做不出来。
Problem 1: 潜伏者

R国和S国正陷入战火之中,双方都互派间谍,潜入对方内部,伺机行动。
历经艰险后,潜伏于S国的R国间谍小C终于摸清了S国军用密码的编码规则:
1、 S国军方内部欲发送的原信息经过加密后在网络上发送,原信息的内容与加密后所的内容均由大写字母‘A’—‘Z’构成(无空格等其他字母)。
2、 S国对于每个字母规定了对应的“密字”。加密的过程就是将原信息中的所有字母替换为其对应的“密字”。
3、 每个字母只对应一个唯一的“密字”,不同的字母对应不同的“密字”。“密字”可以和原字母相同。
例如,若规定‘A’的密字为‘A’,‘B’的密字为‘C’(其他字母及密字略),则原信息“ABA”被加密为“ACA”。
现在,小C通过内线掌握了S国网络上发送的一条加密信息及其对应的原信息。小C希望能通过这条信息,破译S国的军用密码。小C的破译过程是这样的:扫描原信息,对于原信息中的字母x(代表任一大写字母),找到其在加密信息中的对应大写字母y,并认为在密码里y是x的密字。如此进行下去直到停止于如下的某个状态:
1、 所有信息扫描完毕,‘A’—‘Z’所有26个字母在原信息中均出现过并获得了相应的“密字”。
2、 所有信息扫描完毕,但发现存在某个(或某些)字母在原信息中没有出现。
3、 扫描中发现掌握的信息里有明显的自相矛盾或错误(违反S过密码的编码规则)。例如某条信息“XYZ”被翻译为“ABA”就违反了“不同字母对应不同密字”的规则。
在小C忙得头昏脑胀之际,R国司令部又发来电报,要求他翻译另外一条从S国刚刚截取到的加密信息。现在请你帮助小C:通过内线掌握的信息,尝试破译密码。然后利用破译的密码,翻译电报中的加密信息。

输入文件名为spy.in,共3行,每行为一个长度在1到100之间的字符串。
第1行为小C掌握的一条加密信息。
第2行为第1行的加密信息所对应的原信息。
第3行为R国司令部要求小C翻译的加密信息。
输入数据保证所有字符串仅由大写字母‘A’—‘Z’构成,且第1行长度与第2行相等。 

输出文件spy.out共1行。
若破译密码停止时出现2,3两种情况,请你输出“Failed”(不含引号,注意首字母大写,其它小写)。
否则请输出利用密码翻译电报中加密信息后得到的原信息

存正向翻译和逆向翻译,因为两边翻译矛盾都会错误
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
char s1[105],s2[105],x[105],s3[105];
char mima[30],scre[30];
int main() {
    freopen("spy.in","r",stdin);
    freopen("spy.out","w",stdout);
    scanf("%s",s1);
    scanf("%s",s2);
    scanf("%s",s3);
    int len3 = strlen(s3);
    int len1 = strlen(s1);
    int len2 = strlen(s2);
    if(len1 != len2){ printf("Failed");return 0; }
    for(int i = 0; i < len1; i++ ) {
        if(mima[s2[i]-64] != 0 && mima[s2[i]-64] != s1[i]){ printf("Failed");return 0; }
        if(scre[s1[i]-64] != 0 && scre[s1[i]-64] != s2[i]){ printf("Failed");return 0; }
        if(scre[s1[i]-64] == 0) scre[s1[i]-64] = s2[i];
        if(mima[s2[i]-64] == 0) mima[s2[i]-64] = s1[i];
    }
    for(int i = 1; i <= 26; i++ ) if(!scre[i]){ printf("Failed");return 0; }
    for(int i = 0; i < len3; i++ )
        if( !scre[s3[i]-64] ){ printf("Failed");return 0; }
    for(int i = 0; i < len3; i++ )
        printf("%c",scre[s3[i]-64]);
    return 0;
}

Problem 2: Hankson’s Problem

已知正整数a0,a1,b0,b1,设某未知正整数x满足:
    1、 x和a0的最大公约数是a1;
    2、 x和b0的最小公倍数是b1。
    Hankson的“逆问题”就是求出满足条件的正整数x。但稍加思索之后,他发现这样的x并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的x的个数。请你帮助他编程求解这个问题。

因为是很大的数,穷举绝对会T掉,但我们知道数的唯一分解定理,可以筛法求出前40000个素数,存在一个数组里面,找每一个素数的值再在总数上乘一个这个值,如果不符合,就乘一个0.
#include<cmath>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define LL long long;
using namespace std;
int T,a0,a1,b0,b1;
int pri[200005],pd[200005],top,ans;
void fdprime() {
    for(int i = 2; i <= 40000; i++ ) {
        if(!pd[i]){top++;pri[top]=i;}
        for(int j = 1; j <= top && pri[j]*i <= 40000; j++ ) {
            pd[pri[j]*i]=1;
            if(i%pri[j]==0) break;
        }
    }
}
void find(int x) {
    int c0 = 0,c1 = 0,c2 = 0,c3 = 0;
    while(a0 % x == 0) {a0 /= x; c0++;}
    while(a1 % x == 0) {a1 /= x; c1++;}
    while(b0 % x == 0) {b0 /= x; c2++;}
    while(b1 % x == 0) {b1 /= x; c3++;}
    if(c1 > c0 || c2 > c3) ans *= 0;
    else if(c0 == c1 && c2 == c3) {
        if (c1 <= c3) ans *= c3-c1+1;
        else ans *= 0;
    }
    else if(c0==c1&&c2<c3) {
        if (c1<=c3) ans*=1;
        else ans*=0;
    }
    else if(c0>c1&&c2==c3) {
        if (c1<=c3) ans*=1;
        else ans*=0;
    }
    else if (c1 != c3) ans*=0;
}
void work() {
    int i; ans=1;
    for( i = 1; i <= top; i++ ) find(pri[i]);
    if(a0 != 1) find(a0);
    if(b1 != 1) find(b1);
    printf("%lld\n",ans);
}
int main(){
    freopen("son.in","r",stdin);
    freopen("son.out","w",stdout);
    scanf("%d",&T);
    fdprime();
    while(T--){
        scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
        work();
    }
    return 0;
}

Problem3 最优贸易

【问题描述】
C 国有n 个大城市和m 条道路,每条道路连接这n 个城市中的某两个城市。任意两个
城市之间最多只有一条道路直接相连。这m 条道路中有一部分为单向通行的道路,一部分
为双向通行的道路,双向通行的道路在统计条数时也计为1 条。
C 国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价
格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。
商人阿龙来到 C 国旅游。当他得知同一种商品在不同城市的价格可能会不同这一信息
之后,便决定在旅游的同时,利用商品在不同城市中的差价赚回一点旅费。设C 国n 个城
市的标号从1~ n,阿龙决定从1 号城市出发,并最终在n 号城市结束自己的旅行。在旅游的
过程中,任何城市可以重复经过多次,但不要求经过所有n 个城市。阿龙通过这样的贸易方
式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品——水晶球,并在之后经过的另
一个城市卖出这个水晶球,用赚取的差价当做旅费。由于阿龙主要是来C 国旅游,他决定
这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。
假设 C 国有5 个大城市,城市的编号和道路连接情况如下图,单向箭头表示这条道路
为单向通行,双向箭头表示这条道路为双向通行。
假设 1~n 号城市的水晶球价格分别为4,3,5,6,1。
阿龙可以选择如下一条线路:1->2->3->5,并在2 号城市以3 的价格买入水晶球,在3
号城市以5 的价格卖出水晶球,赚取的旅费数为2。
阿龙也可以选择如下一条线路 1->4->5->4->5,并在第1 次到达5 号城市时以1 的价格
买入水晶球,在第2 次到达4 号城市时以6 的价格卖出水晶球,赚取的旅费数为5。
现在给出 n 个城市的水晶球价格,m 条道路的信息(每条道路所连接的两个城市的编号
以及该条道路的通行情况)。请你告诉阿龙,他最多能赚取多少旅费。
输入描述 Input Description
第一行包含 2 个正整数n 和m,中间用一个空格隔开,分别表示城市的数目和道路的
数目。
第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这n 个城
市的商品价格。
接下来 m 行,每行有3 个正整数,x,y,z,每两个整数之间用一个空格隔开。如果z=1,
表示这条道路是城市x 到城市y 之间的单向道路;如果z=2,表示这条道路为城市x 和城市
y 之间的双向道路。
输出描述 Output Description
包含1 个整数,表示最多能赚取的旅费。如果没有进行贸易,
则输出0。
样例输入 Sample Input
5 5
4 3 5 6 1
1 2 1
1 4 1
2 3 2
3 5 1
4 5 2
样例输出 Sample Output
5
数据范围及提示 Data Size & Hint
1≤n≤100000,1≤m≤500000,水晶球价格≤100

其实我是不想写这道题的题解的,这道题为什么与图论有关,因为它有可能有强连通分量,缩点后就是很弱鸡了,前五个点就不用缩,但我还是做错了真是悲哀,现在也没用tarjan写,只用了个双向SPFA一个方向搜最小值,一个方向搜最大值就可以了,注意要用两个邻接表存。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int MX = 100005;
int ans;
int value[MX],head[MX*2],vest[MX*2],next[MX*2],tail = 1;
int had[MX*2],vast[MX*2],naxt[MX*2],minn[MX*2],maxn[MX*2];
bool b[MX*2];
void adde(int a,int b) {
    vest[tail] = b;
    next[tail] = head[a];
    head[a] = tail;
    vast[tail] = a;
    naxt[tail] = had[b];
    had[b] = tail;
    tail++;
}
void spfa(int s,int t) {
    queue<int>que;
    memset(minn,127/3,sizeof(minn));
    memset(b,0,sizeof(b));
    minn[s] = value[s]; b[s] = 1; que.push(s);
    while( !que.empty() ) {
        int u=que.front(); que.pop();
        for(int v = head[u]; v; v = next[v]){
            if(minn[vest[v]] > minn[u]) {
                minn[vest[v]] = min(minn[u], value[vest[v]]);
                if( !b[vest[v]] ) {
                    b[vest[v]] = 1;
                    que.push(vest[v]);
                }
            }
          }
     }
}
void SPFA(int s,int t) {
    queue<int>que;
    memset(maxn,0,sizeof(maxn));
    memset(b,0,sizeof(b));
    maxn[s]=value[s]; b[s]=1; que.push(s);
    while(!que.empty()) {
        int u=que.front(); que.pop();
        for(int v = had[u]; v; v = naxt[v]){
            if(maxn[vast[v]] < maxn[u]) {
                maxn[vast[v]] = max(maxn[u],value[vast[v]]);
                if( !b[vast[v]] ) {
                    b[vast[v]]=1;
                    que.push(vast[v]);
                }
            }
        }
     }
}
int main() {
    freopen("trade.in","r",stdin);
    freopen("trade.out","w",stdout);
    int n,m;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++ )
        scanf("%d", &value[i]);
    for(int i = 1; i <= m; i++ ) {
        int q,w,e;
        scanf("%d%d%d", &q, &w, &e);
        adde(q,w);
        if(e == 2) adde(w,q);
    }
    spfa(1,n);
    SPFA(n,1);//从终点回来
    for(int i = 1; i <= n; i++ ) 
        ans = max(ans,maxn[i]-minn[i]);
    printf("%d", ans);
    return 0;
}
//Tarjan
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 100010, MAXM = 1000010;
int N, M, ans;

int head[MAXN*2],next[MAXN*2],vast[MAXN*2],tail;
void addedge(int a,int b) {
    tail++;
    next[tail] = head[a];
    head[a] = tail;
    vast[tail] = b;
}

int belong[MAXN], bcnt;
int bmx[MAXN], bmn[MAXN];
int tmr, dfn[MAXN], low[MAXN];
int sta[MAXN], tp;
int val[MAXN];
int x[MAXM], y[MAXM], type[MAXM];
bool insta[MAXN];

void tarjan(int u) {
    dfn[u] = low[u] = ++tmr;
    sta[++tp] = u;
    insta[u] = 1;
    for (int i = head[u]; i; i = next[i]) {
        int v = vast[i];
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if (insta[v])
            low[u] = min(low[u], dfn[v]);
    }
    if (low[u] == dfn[u]) {
        ++bcnt;
        int i;
        do {
            i = sta[tp];
            belong[i] = bcnt;
            bmx[bcnt] = max(bmx[bcnt], val[i]);
            bmn[bcnt] = min(bmn[bcnt], val[i]);
            insta[i] = 0; --tp;
        } while (tp>0 && i!=u);
    }
}

int f[MAXN];
bool vis[MAXN];
void dfs(int u) {
    vis[u] = 1;
    if (u == belong[N]) f[u] = max(f[u], bmx[u]);
    for (int p = head[u]; p; p = next[p]) {
        if (!vis[vast[p]]) dfs(vast[p]);
        f[u] = max(f[u], f[vast[p]]);
    }
    if (f[u]) f[u] = max(f[u], bmx[u]);
    ans = max(ans, f[u] - bmn[u]);
}

int main() {
    int i;
    scanf("%d%d", &N, &M);
    for (i = 1; i <= N; ++i) {
        scanf("%d", val+i);
        bmx[i]=-1, bmn[i]=999999;
    }
    for (i = 1; i <= M; ++i) {
        scanf("%d%d%d", &x[i], &y[i], &type[i]);
        addedge(x[i], y[i]);
        if (type[i] == 2) addedge(y[i], x[i]);
    }
    tarjan(1);
    memset(head,0,sizeof(head));
    memset(vast,0,sizeof(vast));
    memset(next,0,sizeof(next));
    tail = 0;
    for (i = 1; i<= M; ++i)
        if (belong[x[i]] != belong[y[i]])
            addedge(belong[x[i]], belong[y[i]]);
    dfs(belong[1]);
    printf("%d\n", ans);
    return 0;
}

Problem4:靶形数独

题目略

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
using namespace std;
int ans=-1,a[10][10],b[10][10],c[10][10];
int f1[10][10],f2[10][10],f3[10][10];
void work(int deft) {//深搜
    int s = 0,x1,y1,ds = 10;
    for(int i = 1; i <= 9; i++ )
        for(int j = 1; j <= 9; j++ ) 
            if(a[i][j] == 0){
                int s = 0;
                for(int k = 1; k <= 9; k++ ) 
                    if(!f1[c[i][j]][k] && !f2[i][k] && !f3[j][k] )
                        s++;
                    if(s < ds) ds = s,x1 = i,y1 = j;//存在情况最少的点
            }
    if(ds == 10) {//点都找完了
        int k = 0;
        for(int i = 1; i <= 9; i++ )
            for(int j = 1; j <= 9; j++ ) 
                k += a[i][j] * b[i][j];
        ans = max(ans,k);
        return ;
    }
    if(ds == 0) return ;
    for(int k = 1; k <= 9; k++ )
        if( !f1[c[x1][y1]][k] && !f2[x1][k] && !f3[y1][k]) {
            a[x1][y1] = k;
            f1[c[x1][y1]][k] = f2[x1][k] = f3[y1][k] = 1;
            work(deft+1);
            a[x1][y1] = 0;
            f1[c[x1][y1]][k] = f2[x1][k] = f3[y1][k] = 0;
        }
}
int main()
{
    freopen("sudoku.in","r",stdin);
    freopen("sudoku.out","w",stdout);
    for(int i=1;i<=9;i++)
        for(int j=1;j<=9;j++) {
            scanf("%d",&a[i][j]);
            b[i][j]=10-max(abs(i-5),abs(j-5));//存分数
            c[i][j]=(i-1)/3*3+(j-1)/3+1;//分成9个九宫格
            f1[c[i][j]][a[i][j]]=1;//该小九宫格存进該点
            f2[i][a[i][j]]=1;//列上的
            f3[j][a[i][j]]=1;//存行上的
        }
    work(1);
    printf("%d",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值