NOIP2009题解
T1:潜伏者
题目大意:给出一段密文和破译后的明文,一个字母对应一个密文字母,要求破译一段密文,如果有矛盾或有未出现密文无法破译输出failed,否则输出明文。
思路:纯模拟题
T2:Hankson的趣味题
题目大意:给出a0,a1,b0,b1求满足条件的x的个数;
条件:gcd(x,a0)=a1;x,b0最小公倍数为b1;
思路:分解质因数,假设对于一个质因数y,a0含有a0c个y,a1含有a1c个y,b0含有b0c个y,b1含有b1c个y。
那么不难得到,如果a0c<a1c,那么就无解;如果a0c=a1c,那么x至少含有a1c个y;如果a0c>a1c,那么x只可能含有a1c个y。
同理,如果b1c<b0c,那么就无解;如果b1c=b0c,那么x至多含有b1c个y;如果b1c>b0c,那么x只可能含有b1c个y。
由此,可以求出对于每一个质数,x可能含有几个它,并求出一共有多少种选择方式。然后根据乘法原理,将每一个质数的选择方案数乘起来,就得到了答案。
----------------------------------------------------------
2017.5.6补充题解:
今天又做了一遍,然后对上面自己的做法一脸懵逼,估计是之前就没看懂吧然后贴了题解???
然后就各种焦虑,物色到了一个超级有道理的题解:
可以证明gcd(x/a1,a0/a1)==1,显然如果不等于1,则gcd(x,a0)<a1;
然后可以证明gcd(b1/x,b1/b0)==1,显然如果不等于1,则lcm(x,b0)<b1;
这样的话,显然就可以枚举b1的所有因数,sqrt(b1),加上gcd复杂度是logb1,所以单组数据复杂度是sqrt(b1)*log2(b1).
T3:最优贸易
题目大意:n个城市m条路,有单向和双向,水晶球在每个城市价格不同,求从城市1到n能获得的最大差价,每个城市可走多次。
思路,边数太多,所以用SPFA,正向做一次取最小,把路都反过来从n倒做一次取最大。在枚举i得到max(max[i]-min[i]);
AC代码:
#include <iostream> using namespace std; int num[100000]; int map[1000000],nxt[1000000]; int las; int head[100000]; int map2[1000000],nxt2[1000000]; int las2; int head2[100000]; void adad(int a,int b) { map[las]=b; nxt[las]=head[a]; head[a]=las++; } void adad2(int a,int b) { map2[las2]=b; nxt2[las2]=head2[a]; head2[a]=las2++; } #define Q_MAX 100000 int used[100000]; int queue[Q_MAX]; int h,r; void enq(int k) { if (used[k]) return; used[k]=1; queue[r]=k; r=(r+1)%Q_MAX; } int exq() { int t; t=queue[h]; used[t]=0; h=(h+1)%Q_MAX; return t; } int minn[100000]; int maxn[100000]; int main() { int i,j; int n,m; int a,b,c; cin >> n >> m; for (i=0;i<n;i++) { head[i]=head2[i]=-1; maxn[i]=-100000; minn[i]=0xFFFFFFF; cin >> num[i]; } for (i=0;i<m;i++) { cin >> a >> b >> c; a--,b--; if (c==2) { adad(a,b); adad(b,a); adad2(a,b); adad2(b,a); } else { adad(a,b); adad2(b,a); } } minn[0]=num[0]; enq(0); while (h!=r) { i=exq(); for (a=head[i];a!=-1;a=nxt[a]) { j=map[a]; if (minn[j]>minn[i]) { minn[j]=minn[i]; enq(j); } if (minn[j]>num[j]) { minn[j]=num[j]; enq(j); } } } maxn[n-1]=num[n-1]; enq(n-1); while (h!=r) { i=exq(); for (a=head2[i];a!=-1;a=nxt2[a]) { j=map2[a]; if (maxn[j]<maxn[i]) { maxn[j]=maxn[i]; enq(j); } if (maxn[j]<num[j]) { maxn[j]=num[j]; enq(j); } } } for (i=a=0;i<n;i++) if (a<maxn[i]-minn[i]) a=maxn[i]-minn[i]; cout << a << "\n"; return 0; }
T4:靶形数独
题目大意:解一个多解数独,从内向外分数不同,求分数最大的解法。
思路:解法肯定还是一样的,dfs。然后比较分数大小输出最大的。
然而这样会超时,codevs上可以过但是最慢的点用了2400多ms,时限4秒。
借鉴了一下某位神牛,用位运算加快运算速度(难写啊啊啊),试了一下还是能过的。
AC代码:
#include<iostream> #include<cmath> using namespace std; int h[10]={},hs[10]={},zs[10]={},xj[5][5]={},hq[10]={}; int ans=-1,st[10],a[10][10]; void make() { int i,j,sum=0; for (i=1;i<5;i++) { for (j=i;j<11-i;j++) sum+=(a[i][j]+a[10-i][j])*(5+i); for (j=i+1;j<10-i;j++) sum+=(a[j][i]+a[j][10-i])*(5+i); } sum+=a[5][5]*10; if (sum>ans) ans=sum; } void dfs(int k) { if (k==10) make(); else { int x,y,j,pos,p,i=st[k]; x=511-h[i]; y=x&-x; h[i]|=y; j=(int)log2(y)+1; pos=511-(hs[i]|zs[j]|xj[(i-1)/3][(j-1)/3]); while (pos>0) { p=pos&-pos; pos-=p; a[i][j]=(int)log2(p)+1; hs[i]|=p; zs[j]|=p; xj[(i-1)/3][(j-1)/3]|=p; if (x==y) dfs(k+1); else dfs(k); hs[i]-=p; zs[j]-=p; xj[(i-1)/3][(j-1)/3]-=p; } h[i]-=y; } } int main() { int i,j,p0; for (i=1;i<10;i++) for (j=1;j<10;j++) { cin >> a[i][j]; if (a[i][j]>0) { h[i]|=1<<(j-1); p0=1<<(a[i][j]-1); if (((hs[i]&p0)!=0)||((zs[j]&p0)!=0)||((xj[(i-1)/3][(j-1)/3]&p0)!=0)) { cout << "-1\n"; return 0; } hs[i]|=p0; zs[j]|=p0; xj[(i-1)/3][(j-1)/3]|=p0; } else hq[i]++; } for (i=1;i<10;i++) st[i]=i; for (i=1;i<9;i++) for (j=i+1;j<10;j++) if (hq[st[i]]>hq[st[j]]) { st[i]^=st[j]; st[j]^=st[i]; st[i]^=st[j]; } i=1; while (hq[st[i]]==0) i++; dfs(i); cout << ans << "\n"; return 0; }