题解
今天做了一份2012day1的真题,我真的没有想到day1会这么难ojz。玄学倍增教你做人。
第一题——Vigenère 密码(vigenere)
- 就是给你一个图,然后给你密码和key,根据图求原码。十分简单的字符串题。
- 其实很简单的。由图上可知,x+y=z+1(xyz分别表示原码、key、和密码)
- 那么x=z+1-y。
- 由于有两遍重复那么就要判断有没有要多加一个26的。
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void fff(){
freopen("vigenere.in","r",stdin);
freopen("vigenere.out","w",stdout);
}
const int MAXN=1010;
char m[MAXN],k[MAXN],c[MAXN],t[MAXN];
int main(){
fff();
cin>>k>>c;
int lc=strlen(c),lk=strlen(k);
for (int i=0;i<lc;i++){
t[i]=c[i];
if(c[i]>='a'&&c[i]<='z') c[i]+=('A'-'a');
}
for (int i=0;i<lk;i++){
if(k[i]>='a'&&k[i]<='z') k[i]+=('A'-'a');
}
int temp=0;
for (int i=0;i<lc;i++){
m[i]=c[i]-k[temp++]+'A';
if(m[i]<'A') m[i]+=26;
if(t[i]>='a'&&t[i]<='z') m[i]-=('A'-'a');
if(temp>=lk) temp=0;
}
cout<<m;
return 0;
}
第二题——国王游戏(game)
- 这是一道神奇的排序题(第一眼以为是dp)然后一直在想方程。
题目大意:告诉你国王和若干大臣的左右手上的数值,除了国王在第一个外求排列使得 Max(∏i−1j=0Lj/Ri M a x ( ∏ j = 0 i − 1 L j / R i 最小。
然后我就使劲想方程ojz,日常跑偏。
- 可以证明按照 L1R1<L2R2 L 1 R 1 < L 2 R 2 排列为最优解。
- 然后就打把高精解决问题。
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void fff(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
}
const int MAXN=1010;
const int LIMIT=10000;
int n;
struct node{
int l,r,num;
bool operator <(const node x) const{
if(num==x.num){
if(l==x.l)return r<x.r;
return l<x.l;
}
return num<x.num;
}
}a[MAXN];
int sum[MAXN<<2],siz_sum,ans[MAXN<<2],siz_ans,maxx[MAXN<<2],siz_maxx;
void multy(int x){
int c=0;
for (int i=1;i<=siz_sum;i++){
sum[i]=sum[i]*x+c;
c=sum[i]/LIMIT;
sum[i]=sum[i]%LIMIT;
}
while (c>0){
sum[++siz_sum]=c%LIMIT;
c/=LIMIT;
}
}
void div(int x){
memcpy(&ans,&sum,siz_sum*sizeof(int));
int c=0;
siz_ans=siz_sum;
for (int i=siz_sum;i>=1;i--){
ans[i]=(c*LIMIT+sum[i])/x;
c=(c*LIMIT+sum[i])%x;
}
}
bool check(){
if(siz_maxx>siz_ans) return false;
else if(siz_maxx<siz_ans) return true;
else{
for (int i=siz_maxx;i>=1;i--){
if(maxx[i]==ans[i]) continue;
if(maxx[i]>ans[i]) return false;
return true;
}
}
}
int main(){
fff();
cin>>n;
for (int i=0;i<=n;i++){
scanf("%d%d",&a[i].l,&a[i].r);
a[i].num=a[i].l*a[i].r;
}
sort(a+1,a+n+1);
sum[1]=a[0].l;
siz_sum=siz_maxx=siz_ans=1;
for (int i=2;i<=n;i++){
multy(a[i-1].l);
div(a[i].r);
if(check()){
siz_maxx=max(siz_ans,siz_maxx);
for (int i=1;i<=siz_maxx;i++) maxx[i]=ans[i];
}
}
while (maxx[siz_maxx]==0&&siz_maxx>=0) siz_maxx--;
if(maxx[1]==0&&siz_maxx==-1) cout<<1;
else
for (int i=siz_maxx;i>=1;i--){
if(i!=siz_maxx) printf("%04d",maxx[i]);
else printf("%d",maxx[i]);
}
return 0;
}
第三题——开车旅行(drive)
- 这是题变态题(还是不知道为什么放在day1)题解说是用离散化预处理和倍增。
- 其实有两小问。第一小问是给你x0,让你求哪个点出发的AB比值最小。
- 第二小问是给定出发点和x,求两个人走的路。
这道题果断暴力也能够拿70分。但玄学倍增能够拿满分ojz。
预处理先找出从第i点出发向后的最近点和次近点。然后根据这一种方法进行st倍增做法。得到答案。
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#define ll long long
using namespace std;
void fff(){
freopen("drive.in","r",stdin);
freopen("drive.out","w",stdout);
}
const int MAXN=100100;
struct node{
int h,id,l,r;
bool operator < (const node x) const{
return h<x.h;
}
}a[MAXN];
int n,m;
ll x;
int p[MAXN],s,nb[MAXN],na[MAXN];
int f[MAXN][21],stA[MAXN][21],stB[MAXN][21];
int l,r,j;//Pascal选手的全局变量在离散化情况下的效率会提高
ll abs1(int a){
if(a<0) a=-a;
return a;
}
bool zuo(){
if(l==0) return false;
if(r==0) return true;
return abs1(a[l].h-a[j].h)<=abs1(a[r].h-a[j].h);
}
int pd(int x,int y){
if(!x) return a[y].id;
if(!y) return a[x].id;
if(abs1(a[j].h-a[x].h)<=abs1(a[j].h-a[y].h)) return a[x].id;
return a[y].id;
}
void st(){
for (int j=1;j<=19;j++){
for (int i=1;i<=n;i++){
f[i][j]=f[f[i][j-1]][j-1];
stA[i][j]=stA[i][j-1]+stA[f[i][j-1]][j-1];
stB[i][j]=stB[i][j-1]+stB[f[i][j-1]][j-1];
}
}
}
int ta,tb,ans;
double minn=9999999999;
void getab(long long x,int p){
ta=tb=0;
for (int i=19;i>=0;i--){
if(f[p][i]&&(ll)(ta+tb+stA[p][i]+stB[p][i])<=x){
ta+=stA[p][i];
tb+=stB[p][i];
p=f[p][i];
}
}
if(na[p]&&ta+tb+stA[p][0]<=x) ta+=stA[p][0];
}
int main(){
fff();
cin>>n;
for (int i=1;i<=n;i++){
scanf("%d",&a[i].h);
a[i].id=i;
}
sort(a+1,a+n+1);
for (int i=1;i<=n;i++){
p[a[i].id]=i;//指向现有的位置
a[i].l=i-1;
a[i].r=i+1;
}
a[1].l=a[n].r=0;
for (int i=1;i<=n;i++){
j=p[i],l=a[j].l,r=a[j].r;
if(zuo()){
nb[i]=a[l].id;
na[i]=pd(a[l].l,r);
}else{
nb[i]=a[r].id;
na[i]=pd(l,a[r].r);
}
if(l) a[l].r=r;
if(r) a[r].l=l;
}
for (int i=1;i<=n;i++){
f[i][0]=nb[na[i]];
stA[i][0]=abs1(a[p[i]].h-a[p[na[i]]].h);
stB[i][0]=abs1(a[p[f[i][0]]].h-a[p[na[i]]].h);
}
st();
cin>>x>>m;
for (int i=1;i<=n;i++){
getab(x,i);
if(tb&&1.0*ta/tb<minn){
ans=i;
minn=1.0*ta/tb;
}
}
printf("%d\n",ans);
for (int i=1;i<=m;i++){
scanf("%d%lld",&s,&x);
getab(x,s);
printf("%d %d\n",ta,tb);
}
return 0;
}