NOIP2012题解
NOIP2012题解
Day1
Vigenère 密码 vigenere
直接模拟就好了,对于那张表找找规律就很短了。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 1010
char k[MAX],c[MAX];
char get(char a,char b){return (((b-97)-(a-97)+26)%26)+97;}
int main()
{
scanf("%s",k);scanf("%s",c);
for(int i=0,l=strlen(c),lk=strlen(k);i<l;++i)
{
int x=0;
if(c[i]>='A'&&c[i]<='Z')c[i]+=32,x=-32;
if(k[i%lk]>='A'&&k[i%lk]<='Z')k[i%lk]+=32;
putchar(get(k[i%lk],c[i])+x);
}
puts("");return 0;
}
国王游戏 game
考虑两个人的先后顺序对于答案的影响。分类讨论之后不难发现每个人按照左手乘右手的值从小往大排序显然最优。高精度计算答案即可。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 1050
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n;
struct peo{int l,r;}p[MAX];
bool operator<(peo a,peo b){return 1ll*a.l*a.r<1ll*b.l*b.r;}
struct Bigint
{
int s[5000],ws;
void init(){s[ws=1]=1;}
void Multi(int b)
{
for(int i=1;i<=ws;++i)s[i]*=b;
for(int i=1;i<=ws;++i)s[i+1]+=s[i]/10,s[i]%=10;
while(s[ws+1])++ws,s[ws+1]+=s[ws]/10,s[ws]%=10;
}
void output(){for(int i=ws;i;--i)printf("%d",s[i]);puts("");}
}ans,now;
bool operator<(Bigint a,Bigint b)
{
if(a.ws!=b.ws)return a.ws<b.ws;
for(int i=a.ws;i;--i)
if(a.s[i]!=b.s[i])return a.s[i]<b.s[i];
return false;
}
Bigint operator/(Bigint a,int b)
{
int r=0;
for(int i=a.ws;i;--i)
{
r=r*10+a.s[i];
a.s[i]=r/b;r%=b;
}
while(a.ws>1&&!a.s[a.ws])--a.ws;
return a;
}
int main()
{
n=read();now.init();now.Multi(read());read();
for(int i=1;i<=n;++i)p[i].l=read(),p[i].r=read();
sort(&p[1],&p[n+1]);
for(int i=1;i<=n;++i)
{
ans=max(ans,now/p[i].r);
now.Multi(p[i].l);
}
ans.output();
return 0;
}
开车旅行 drive
好题。
预处理出在每个位置\(A\)和\(B\)分别会走到哪里,可以用链表实现。接下来把\(A,B\)分别走一次看做一组,直接倍增求解即可。注意最后\(A\)还可能可以单独走一次。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 100100
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n;
int h[MAX];
int dA[MAX],dB[MAX];
namespace Destination
{
int nt[MAX],lt[MAX],p[MAX],id[MAX];
bool cmp(int a,int b){return h[a]<h[b];}
void upd(int &mx,int &mxx,int i,int j)
{
int s=abs(h[i]-h[j]);if(!j)return;
if(!mx||s<abs(h[i]-h[mx])||(s==abs(h[i]-h[mx])&&h[j]<h[mx]))swap(mx,mxx),mx=j;
else if(!mxx||s<abs(h[i]-h[mxx])||(s==abs(h[i]-h[mxx])&&h[j]<h[mxx]))mxx=j;
}
void Work()
{
for(int i=1;i<=n;++i)p[i]=i;
sort(&p[1],&p[n+1],cmp);
for(int i=1;i<=n;++i)lt[p[i]]=p[i-1],nt[p[i]]=p[i+1];
for(int i=1;i<=n;++i)
{
int mx=0,mxx=0;
upd(mx,mxx,i,lt[i]);upd(mx,mxx,i,lt[lt[i]]);
upd(mx,mxx,i,nt[i]);upd(mx,mxx,i,nt[nt[i]]);
nt[lt[i]]=nt[i];lt[nt[i]]=lt[i];lt[0]=nt[0]=0;
dA[i]=mxx;dB[i]=mx;
}
}
}
int p[17][MAX],sA[17][MAX],sB[17][MAX];
void Query(int S,int X,int &disA,int &disB)
{
disA=disB=0;
for(int i=16;~i;--i)
if(p[i][S]&&X>=sA[i][S]+sB[i][S])
disA+=sA[i][S],disB+=sB[i][S],X-=sA[i][S]+sB[i][S],S=p[i][S];
if(dA[S]&&X>=abs(h[S]-h[dA[S]]))
disA+=abs(h[S]-h[dA[S]]);
}
int main()
{
n=read();
for(int i=1;i<=n;++i)h[i]=read();
Destination::Work();
for(int i=1;i<=n;++i)
{
p[0][i]=dB[dA[i]];
sA[0][i]=abs(h[i]-h[dA[i]]);
sB[0][i]=abs(h[dA[i]]-h[dB[dA[i]]]);
}
for(int i=1;i<17;++i)
for(int j=1;j<=n;++j)
{
p[i][j]=p[i-1][p[i-1][j]];
sA[i][j]=sA[i-1][j]+sA[i-1][p[i-1][j]];
sB[i][j]=sB[i-1][j]+sB[i-1][p[i-1][j]];
}
int X=read();double Rate=1e18;int pos=0;
for(int i=1;i<=n;++i)
{
int disA,disB;double V=0;
Query(i,X,disA,disB);
if(!disB)V=1e18-1;
else V=1.0*disA/disB;
if(V<Rate)Rate=V,pos=i;
else if(V==Rate&&h[i]>h[pos])pos=i;
}
printf("%d\n",pos);
int Q=read();
while(Q--)
{
int S=read(),X=read(),disA,disB;
Query(S,X,disA,disB);
printf("%d %d\n",disA,disB);
}
return 0;
}
Day2
同余方程 mod
\(exgcd\)模板题
#include<iostream>
using namespace std;
int a,b;
void exgcd(int a,int b,int &x,int &y){if(a==0){x=0;y=1;return;}exgcd(b%a,a,y,x);x-=b/a*y;}
int inv(int a,int b){int x,y;exgcd(a,b,x,y);x=(x%b+b)%b;return x;}
int main()
{
cin>>a>>b;
cout<<inv(a,b)<<endl;
return 0;
}
借教室 classroom
二分答案,差分\(check\)。
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 1000100
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,m,s[MAX],d[MAX],l[MAX],r[MAX],a[MAX];
bool check(int k)
{
for(int i=1;i<=n;++i)a[i]=0;
for(int i=1;i<=k;++i)a[l[i]]+=d[i],a[r[i]+1]-=d[i];
for(int i=1;i<=n;++i){a[i]+=a[i-1];if(a[i]>s[i])return false;}
return true;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;++i)s[i]=read();
for(int i=1;i<=m;++i)d[i]=read(),l[i]=read(),r[i]=read();
int l=1,r=m,ret=m+1;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))l=mid+1;
else ret=mid,r=mid-1;
}
if(ret>m)puts("0");else printf("-1\n%d\n",ret);
return 0;
}
疫情控制 blockade
好题。
不难发现可以二分答案。考虑如何\(check\)。
首先如果一支军队不能走到根节点那么显然它就停在能够到达的深度最浅的位置一定最优。那么军队分为两类,一类可以到达根节点,即可以控制其他子树,另外一类则不行,那么停留在最优位置不动。接下来,先不考虑可以控制其他子树的军队,\(dfs\)一遍,看看哪些子树需要控制。显然控制在根节点的儿子位置一定最优。显然所有可以控制其他子树的军队全部到达根节点,然后按照剩余移动时间排序。所有需要被覆盖的子树按照距离排序。从小往大一一匹配即可。注意一个问题,考虑一支军队的时候,如果它过来的那个子树还未被覆盖,那么优先覆盖他过来的那棵子树。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define MAX 50050
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Line{int v,next,w;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
int n,m,top[MAX],a[MAX];
ll s[16][MAX],dep[MAX];
int p[16][MAX];
void dfs(int u,int ff)
{
dep[u]=dep[ff]+s[0][u];
for(int i=1;i<16;++i)
p[i][u]=p[i-1][p[i-1][u]],s[i][u]=s[i-1][u]+s[i-1][p[i-1][u]];
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff)
p[0][e[i].v]=u,s[0][e[i].v]=e[i].w,dfs(e[i].v,u);
}
void dfs(int u,int ff,int id)
{
top[u]=id;
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff)dfs(e[i].v,u,id);
}
int Move(int u,ll x)
{
for(int i=15;~i;--i)
if(p[i][u]&&x>=s[i][u])
x-=s[i][u],u=p[i][u];
return u;
}
struct Army{int fr;ll t;}q[MAX];
bool operator<(Army a,Army b){return a.t<b.t;}
bool cmp(int a,int b){return dep[a]<dep[b];}
bool vis[MAX];
void dfs2(int u,int ff)
{
bool fl=true;int son=0;
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff)
{
dfs2(e[i].v,u);++son;
if(!vis[e[i].v])fl=false;
}
if(fl&&son)vis[u]=true;
}
int nd[MAX];
bool check(ll t)
{
int tot=0,sum=0;memset(vis,0,sizeof(vis));
for(int i=1;i<=m;++i)
{
int x=Move(a[i],t);
if(x==1)q[++tot]=(Army){top[a[i]],t-dep[a[i]]};
else vis[x]=true;
}
dfs2(1,0);if(vis[1])return true;
for(int i=h[1];i;i=e[i].next)
if(!vis[e[i].v])nd[++sum]=e[i].v;
sort(&q[1],&q[tot+1]);
sort(&nd[1],&nd[sum+1],cmp);
if(tot<sum)return false;int j=1;
for(int i=1;i<=tot;++i)
{
if(!vis[q[i].fr]){vis[q[i].fr]=true;continue;}
while(j<=sum&&vis[nd[j]])++j;
if(q[i].t>=dep[nd[j]])vis[nd[j]]=true;
}
while(j<=sum&&vis[nd[j]])++j;
return j>sum;
}
int main()
{
n=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read(),w=read();
Add(u,v,w);Add(v,u,w);
}
dfs(1,0);
for(int i=h[1];i;i=e[i].next)dfs(e[i].v,1,e[i].v);
m=read();
for(int i=1;i<=m;++i)a[i]=read();
ll l=0,r=5e13,ret=-1;
while(l<=r)
{
ll mid=(l+r)>>1;
if(check(mid))ret=mid,r=mid-1;
else l=mid+1;
}
printf("%lld\n",ret);
return 0;
}