zoukankan      html  css  js  c++  java
  • 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
    ",pos);
    	int Q=read();
    	while(Q--)
    	{
    		int S=read(),X=read(),disA,disB;
    		Query(S,X,disA,disB);
    		printf("%d %d
    ",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
    %d
    ",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
    ",ret);
    	return 0;
    }
    
  • 相关阅读:
    奖学金 题解
    大数加法
    删除倒数第 N 个节点
    css中行内元素默认间隙解决方案
    vuecli3项目中优化lodash/moment使用
    谷歌浏览器input输入框自动填充数据
    vuecli3首页白屏优化
    highcharts开发交易所的行情走势图
    react-native使用flatlist上拉加载下拉刷新
    放大预览图片不失真
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9926288.html
Copyright © 2011-2022 走看看