您的位置:首页 > 其它

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

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

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
) 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: