1.Vigenère密码
一开始以为是明码反暗码,就直接把密码表预处理,然后测了多好多遍还是没过样例,十分困惑与不解然后就看一了一遍题目(咳,
暗码减去密码取模
这一遍很自信然后爆零了
(输出区分大小写
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1010;
char m[maxn],k[maxn];
int lm,lk,key[maxn];
int main()
{
freopen("vigenere.in","r",stdin);
freopen("vigenere.out","w",stdout);
scanf("%s%s",k,m);
lm=strlen(m),lk=strlen(k);
for(int i=0;i<lk;i++)
if(k[i]>='a'&&k[i]<='z') key[i]=k[i]-'a';
else key[i]=k[i]-'A';
int j=0;
for(int i=0;i<lm;i++)
{
char tmp;
if(m[i]>='a'&&m[i]<='z') tmp='a',m[i]-='a';
else tmp='A',m[i]-='A';
m[i]-=key[j];
if(m[i]<0) m[i]+=26;
m[i]+=tmp;
j++;
if(j>=lk) j-=lk;
}
for(int i=0;i<lm;i++)
printf("%c",m[i]);
return 0;
}
2.国王游戏
手推几组数据就很容易得到一个结论:最优的方案肯定是大臣们按成绩升序排序时候的序列,然后要打高精度(想吐。初测六十,位数最高点没处理好
结论摸鱼证明:
A1…Ax-1AxAx+1…Ay-1AyAy+1
设A1到Ax-1乘积为k1,那么Ax的钱就是k1/Bx,
设Ax+1到Ay-1乘积为k2,那么Ay的钱就是k1*Ax*k2/By
那么最优方案就是max(k1/Bx,k1*Ax*k2/By)
讲Ay和Ax互换,同理可得此时最优方案是max(k1/By,k1*k2*Ay/Bx)
易得
k1*Ax*k2/By>k1/By
k1*k2*Ay/Bx>k1/Bx
则
比较k1*Ax*k2/By k1*k2*Ay/Ax
很容易可以判断 大的放后面最优值就最小
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
struct ppl{
ll a,b;
} p[1005];
ll n,x;
ll sum[5000],ans[5000],divn[5000];
bool cmp(ppl x,ppl y){
return x.a*x.b<y.a*y.b;
}
void compare(){
for(ll k=max(ans[0],divn[0]);k>0;k--){
if(ans[k]>divn[k])return;
if(ans[k]<divn[k]){
for(ll j=0;j<=divn[0];j++)
ans[j]=divn[j];
return;
}
}
return;
}
int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%lld",&n);
for(ll i=0;i<=n;i++)scanf("%lld%lld",&p[i].a,&p[i].b);
sort(p+1,p+n+1,cmp);
memset(ans,0,sizeof(ans));
memset(sum,0,sizeof(sum));
sum[0]=1; sum[1]=1;
for(ll i=1;i<=n;i++){
x=0;
for(ll j=1;j<=sum[0];j++){
sum[j]=sum[j]*p[i-1].a+x;
x=sum[j]/10000;
sum[j]%=10000;
}
if(x>0){
sum[0]++;
sum[sum[0]]=x;
}
x=0;
memset(divn,0,sizeof(divn));
for(ll j=sum[0];j>0;j--){
divn[j]=(x*10000+sum[j])/p[i].b;
x=(x*10000+sum[j])%p[i].b;
}
divn[0]=sum[0];
while(divn[divn[0]]==0)divn[0]--;
compare();
}
printf("%lld",ans[ans[0]]);
for(ll i=ans[0]-1;i>0;i--){
printf("%lld",ans[i]/1000);
printf("%lld",ans[i]/100%10);
printf("%lld",ans[i]/10%10);
printf("%lld",ans[i]%10);
}
return 0;
}
3.开车旅行
因为第二题的高精度所以得分 0
可以直接模拟距离每个点最近和次近的点 打得好70
也可以用双向链表模拟 这个很快 但是很容易出错 100
线段树:
线段树维护三个值,区间内最小值、最大值、数的个数。从右往左找(nn~11),当找到第ii个点时,第i+1i+1个点到第nn个点的高度值已经更新过了线段树。通过线段树找出高度比HiHi大的最小、次小值,以及找出高度比HiHi小的最大、次大值,便可以更新第ii个点的最近、次近的点的编号了。因为HiHi的值可能很大,会空间超限,所以做之前需要离散化一次
答案会很大,需要开int64(long long)
然后第二个小问就用倍增
设f[i][j][0]表示从城市i出发,小A经过2^j轮(注意是轮)之后走的路程。
f[i][j][1]表示小B走的路程
g[i][j]表示走到那个城市。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
#define db double
#define ll long long
#define inf 0x7fffffff
using namespace std;
struct node{
int d;ll v;
}p[N];
struct note{
int mx,mi;
}t[N*5];
bool cmp(node x,node y) {
return x.v<y.v||x.v==y.v&&x.d<y.d;
}
db sum,ans;
int n,m,tot,x,y,k;
int h[N],a[N],b[N],w[N],g[N][18];
ll f[N][18][2],ana,anb,v[N];
void change(int v,int l,int r,int x) {
if (l==r) {t[v].mx=t[v].mi=l;return;}
int m=(l+r)/2;
if (x<=m) change(v*2,l,m,x);
else change(v*2+1,m+1,r,x);
t[v].mx=max(t[v*2].mx,t[v*2+1].mx);
t[v].mi=min(t[v*2].mi,t[v*2+1].mi);
}
int getmx(int v,int l,int r,int x,int y) {
if (x>y) return 0;
if (l==x&&r==y) return t[v].mx;
int m=(l+r)/2;
if (y<=m) return getmx(v*2,l,m,x,y);
else if (x>m) return getmx(v*2+1,m+1,r,x,y);
else return max(getmx(v*2,l,m,x,m),getmx(v*2+1,m+1,r,m+1,y));
}int getmi(int v,int l,int r,int x,int y) {
if (x>y) return n+1;
if (l==x&&r==y) return t[v].mi;
int m=(l+r)/2;
if (y<=m) return getmi(v*2,l,m,x,y);
else if (x>m) return getmi(v*2+1,m+1,r,x,y);
else return min(getmi(v*2,l,m,x,m),getmi(v*2+1,m+1,r,m+1,y));
}
void solve(int x,int y) {
ana=anb=0;
fd(j,17,0)
if (f[x][j][0]+f[x][j][1]<=y) {
y-=f[x][j][0]+f[x][j][1];
ana+=f[x][j][0];anb+=f[x][j][1];
x=g[x][j];
}
if (f[x][0][0]<=y) ana+=f[x][0][0];
}
int main() {
freopen("drive.in","r",stdin);
freopen("drive.out","w",stdout);
scanf("%d",&n);
fo(i,1,n)
scanf("%lld",&v[i]),p[i].v=v[i],p[i].d=i;
sort(p+1,p+n+1,cmp);v[0]=inf;
fo(i,1,n) h[p[i].d]=++tot,w[tot]=p[i].d;
fo(i,1,n*5) t[i].mi=n+1;
fd(i,n,1) {
p[1].d=getmi(1,1,n,h[i]+1,n);p[2].d=getmx(1,1,n,1,h[i]-1);
p[3].d=getmi(1,1,n,p[1].d+1,n);p[4].d=getmx(1,1,n,1,p[2].d-1);
fo(j,1,4) p[j].v=abs(v[i]-v[w[p[j].d]]);
sort(p+1,p+5,cmp);
if (p[1].d!=0&&p[1].d!=n+1) b[i]=w[p[1].d];
if (p[2].d!=0&&p[2].d!=n+1) a[i]=w[p[2].d];
change(1,1,n,h[i]);
}
fo(i,1,n) {
g[i][0]=b[a[i]];
f[i][0][0]=abs(v[i]-v[a[i]]);
f[i][0][1]=abs(v[a[i]]-v[b[a[i]]]);
}
fo(j,1,17)
fo(i,1,n) {
g[i][j]=g[g[i][j-1]][j-1];
f[i][j][0]=f[i][j-1][0]+f[g[i][j-1]][j-1][0];
f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1];
}
scanf("%d",&x);ans=inf;
fo(i,1,n) {
solve(i,x);
if (!anb) sum=inf;else sum=ana*1.0/anb;
if (sum<ans||sum==ans&&v[i]>v[k]) ans=sum,k=i;
}
printf("%d\n",k);
for(scanf("%d",&m);m;m--) {
scanf("%d%d",&x,&y);
solve(x,y);
printf("%lld %lld\n",ana,anb);
}
}
基础还是不踏实
一开始以为是明码反暗码,就直接把密码表预处理,然后测了多好多遍还是没过样例,十分困惑与不解然后就看一了一遍题目(咳,
暗码减去密码取模
这一遍很自信然后爆零了
(输出区分大小写
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1010;
char m[maxn],k[maxn];
int lm,lk,key[maxn];
int main()
{
freopen("vigenere.in","r",stdin);
freopen("vigenere.out","w",stdout);
scanf("%s%s",k,m);
lm=strlen(m),lk=strlen(k);
for(int i=0;i<lk;i++)
if(k[i]>='a'&&k[i]<='z') key[i]=k[i]-'a';
else key[i]=k[i]-'A';
int j=0;
for(int i=0;i<lm;i++)
{
char tmp;
if(m[i]>='a'&&m[i]<='z') tmp='a',m[i]-='a';
else tmp='A',m[i]-='A';
m[i]-=key[j];
if(m[i]<0) m[i]+=26;
m[i]+=tmp;
j++;
if(j>=lk) j-=lk;
}
for(int i=0;i<lm;i++)
printf("%c",m[i]);
return 0;
}
2.国王游戏
手推几组数据就很容易得到一个结论:最优的方案肯定是大臣们按成绩升序排序时候的序列,然后要打高精度(想吐。初测六十,位数最高点没处理好
结论摸鱼证明:
A1…Ax-1AxAx+1…Ay-1AyAy+1
设A1到Ax-1乘积为k1,那么Ax的钱就是k1/Bx,
设Ax+1到Ay-1乘积为k2,那么Ay的钱就是k1*Ax*k2/By
那么最优方案就是max(k1/Bx,k1*Ax*k2/By)
讲Ay和Ax互换,同理可得此时最优方案是max(k1/By,k1*k2*Ay/Bx)
易得
k1*Ax*k2/By>k1/By
k1*k2*Ay/Bx>k1/Bx
则
比较k1*Ax*k2/By k1*k2*Ay/Ax
很容易可以判断 大的放后面最优值就最小
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
struct ppl{
ll a,b;
} p[1005];
ll n,x;
ll sum[5000],ans[5000],divn[5000];
bool cmp(ppl x,ppl y){
return x.a*x.b<y.a*y.b;
}
void compare(){
for(ll k=max(ans[0],divn[0]);k>0;k--){
if(ans[k]>divn[k])return;
if(ans[k]<divn[k]){
for(ll j=0;j<=divn[0];j++)
ans[j]=divn[j];
return;
}
}
return;
}
int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%lld",&n);
for(ll i=0;i<=n;i++)scanf("%lld%lld",&p[i].a,&p[i].b);
sort(p+1,p+n+1,cmp);
memset(ans,0,sizeof(ans));
memset(sum,0,sizeof(sum));
sum[0]=1; sum[1]=1;
for(ll i=1;i<=n;i++){
x=0;
for(ll j=1;j<=sum[0];j++){
sum[j]=sum[j]*p[i-1].a+x;
x=sum[j]/10000;
sum[j]%=10000;
}
if(x>0){
sum[0]++;
sum[sum[0]]=x;
}
x=0;
memset(divn,0,sizeof(divn));
for(ll j=sum[0];j>0;j--){
divn[j]=(x*10000+sum[j])/p[i].b;
x=(x*10000+sum[j])%p[i].b;
}
divn[0]=sum[0];
while(divn[divn[0]]==0)divn[0]--;
compare();
}
printf("%lld",ans[ans[0]]);
for(ll i=ans[0]-1;i>0;i--){
printf("%lld",ans[i]/1000);
printf("%lld",ans[i]/100%10);
printf("%lld",ans[i]/10%10);
printf("%lld",ans[i]%10);
}
return 0;
}
3.开车旅行
因为第二题的高精度所以得分 0
可以直接模拟距离每个点最近和次近的点 打得好70
也可以用双向链表模拟 这个很快 但是很容易出错 100
线段树:
线段树维护三个值,区间内最小值、最大值、数的个数。从右往左找(nn~11),当找到第ii个点时,第i+1i+1个点到第nn个点的高度值已经更新过了线段树。通过线段树找出高度比HiHi大的最小、次小值,以及找出高度比HiHi小的最大、次大值,便可以更新第ii个点的最近、次近的点的编号了。因为HiHi的值可能很大,会空间超限,所以做之前需要离散化一次
答案会很大,需要开int64(long long)
然后第二个小问就用倍增
设f[i][j][0]表示从城市i出发,小A经过2^j轮(注意是轮)之后走的路程。
f[i][j][1]表示小B走的路程
g[i][j]表示走到那个城市。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
#define db double
#define ll long long
#define inf 0x7fffffff
using namespace std;
struct node{
int d;ll v;
}p[N];
struct note{
int mx,mi;
}t[N*5];
bool cmp(node x,node y) {
return x.v<y.v||x.v==y.v&&x.d<y.d;
}
db sum,ans;
int n,m,tot,x,y,k;
int h[N],a[N],b[N],w[N],g[N][18];
ll f[N][18][2],ana,anb,v[N];
void change(int v,int l,int r,int x) {
if (l==r) {t[v].mx=t[v].mi=l;return;}
int m=(l+r)/2;
if (x<=m) change(v*2,l,m,x);
else change(v*2+1,m+1,r,x);
t[v].mx=max(t[v*2].mx,t[v*2+1].mx);
t[v].mi=min(t[v*2].mi,t[v*2+1].mi);
}
int getmx(int v,int l,int r,int x,int y) {
if (x>y) return 0;
if (l==x&&r==y) return t[v].mx;
int m=(l+r)/2;
if (y<=m) return getmx(v*2,l,m,x,y);
else if (x>m) return getmx(v*2+1,m+1,r,x,y);
else return max(getmx(v*2,l,m,x,m),getmx(v*2+1,m+1,r,m+1,y));
}int getmi(int v,int l,int r,int x,int y) {
if (x>y) return n+1;
if (l==x&&r==y) return t[v].mi;
int m=(l+r)/2;
if (y<=m) return getmi(v*2,l,m,x,y);
else if (x>m) return getmi(v*2+1,m+1,r,x,y);
else return min(getmi(v*2,l,m,x,m),getmi(v*2+1,m+1,r,m+1,y));
}
void solve(int x,int y) {
ana=anb=0;
fd(j,17,0)
if (f[x][j][0]+f[x][j][1]<=y) {
y-=f[x][j][0]+f[x][j][1];
ana+=f[x][j][0];anb+=f[x][j][1];
x=g[x][j];
}
if (f[x][0][0]<=y) ana+=f[x][0][0];
}
int main() {
freopen("drive.in","r",stdin);
freopen("drive.out","w",stdout);
scanf("%d",&n);
fo(i,1,n)
scanf("%lld",&v[i]),p[i].v=v[i],p[i].d=i;
sort(p+1,p+n+1,cmp);v[0]=inf;
fo(i,1,n) h[p[i].d]=++tot,w[tot]=p[i].d;
fo(i,1,n*5) t[i].mi=n+1;
fd(i,n,1) {
p[1].d=getmi(1,1,n,h[i]+1,n);p[2].d=getmx(1,1,n,1,h[i]-1);
p[3].d=getmi(1,1,n,p[1].d+1,n);p[4].d=getmx(1,1,n,1,p[2].d-1);
fo(j,1,4) p[j].v=abs(v[i]-v[w[p[j].d]]);
sort(p+1,p+5,cmp);
if (p[1].d!=0&&p[1].d!=n+1) b[i]=w[p[1].d];
if (p[2].d!=0&&p[2].d!=n+1) a[i]=w[p[2].d];
change(1,1,n,h[i]);
}
fo(i,1,n) {
g[i][0]=b[a[i]];
f[i][0][0]=abs(v[i]-v[a[i]]);
f[i][0][1]=abs(v[a[i]]-v[b[a[i]]]);
}
fo(j,1,17)
fo(i,1,n) {
g[i][j]=g[g[i][j-1]][j-1];
f[i][j][0]=f[i][j-1][0]+f[g[i][j-1]][j-1][0];
f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1];
}
scanf("%d",&x);ans=inf;
fo(i,1,n) {
solve(i,x);
if (!anb) sum=inf;else sum=ana*1.0/anb;
if (sum<ans||sum==ans&&v[i]>v[k]) ans=sum,k=i;
}
printf("%d\n",k);
for(scanf("%d",&m);m;m--) {
scanf("%d%d",&x,&y);
solve(x,y);
printf("%lld %lld\n",ana,anb);
}
}
基础还是不踏实