zoukankan      html  css  js  c++  java
  • NOIP 2012

    • Prob.1 vigenere密码

      模拟
      代码:

      #include<cstdio>
      #include<cstring>
      #include<iostream>
      using namespace std;
      char K[105],A[1005];
      int main(){
      	scanf("%s",K);int p=0;
      	scanf("%s",A);
      	for(int i=0;A[i];i++){
      		char k=K[p]; if(isupper(k)) k+=32;
      		char a=A[i]; if(isupper(a)) a+=32;
      		for(char c='a';c<='z';c++){
      			char aa=(c-'a'+k-'a') % 26+'a';
      			if(aa!=a) continue;
      			putchar(c-(isupper(A[i])?32:0)); break;
      		}
      		p++; if(!K[p]) p=0;
      	}
      	return 0;
      }
      
    • Prob.2 国王游戏

      贪心,按a*b(左右手权值积)从小到大排序
      正确性证明(交换):
          (排序后)考虑 相邻的两个大臣,
          设第i个大臣的左右手权值分别为 a , b
          设第i+1个大臣的左右手权值分别为 c , d
          则 a*b<c*d
          其他大臣得到的金币数不变,令第i个大臣前面的人的左手权值积为A
          第i个大臣的金币数为  x1=A/b, 第i+1个大臣的金币数为 y1=A*a/d
         
          如果交换i和i+1,则交换后
          第i个大臣的金币数为  y1=A/d, 第i+1个大臣的金币数为 y2=A*c/b
         
          易得 y2>y1>x1且y2>x1,所以交换后,最大值会变大。
          所以不能交换。
      然后就是一个高精度乘和低精度运算的事了。

      代码:

      #include<cstdio>
      #include<cstring>
      #include<iostream>
      #include<algorithm>
      #define bit 10000
      using namespace std;
      struct people{
      	int a,b;
      	bool operator <(const people &rtm) const{
      		return a*b<rtm.a*rtm.b;
      	}
      }p[1005];
      struct Big_int{//高精与低精的运算 
      	int a[1005],len;
      	Big_int(){
      		memset(a,0,sizeof(a)); len=1;
      	}
      	void operator =(int rtm){
      		if(!rtm) return; 
      		len=0; while(rtm){
      			a[++len]=rtm%bit;
      			rtm/=bit;
      		}
      	}
      	bool operator <(const Big_int &rtm) const {
      		if(len!=rtm.len) return len<rtm.len;
      		for(int i=len;i;i--)
      			if(a[i]!=rtm.a[i]) return a[i]<rtm.a[i];
      		return 0;
      	}
      	Big_int operator *(const int &rtm) const {
      		Big_int now; now.len=len+1;
      		for(int i=1;i<=len;i++){ 
      			now.a[i]+=a[i]*rtm;
      			now.a[i+1]+=now.a[i]/bit;
      			now.a[i]%=bit;
      		}
      		while(now.len>1&&!now.a[now.len]) now.len--;
      		return now;
      	}
      	Big_int operator /(const int &rtm){
      		Big_int now; now.len=len; int val=0;
      		for(int i=len;i;i--){
      			val=val*bit+a[i];
      			now.a[i]=val/rtm;
      			val%=rtm;
      		} 
      		while(now.len>1&&!now.a[now.len]) now.len--;
      		return now;
      	}
      	void Print(){
      		printf("%d",a[len]);
      		for(int i=len-1;i;i--)
      			printf("%04d",a[i]);
      		printf("
      ");
      	}
      };
      int main()
      { 
      	//freopen("in.in","r",stdin);
      	int n; scanf("%d",&n);
      	for(int i=0;i<=n;i++) 
      		scanf("%d%d",&p[i].a,&p[i].b);
      	sort(p+1,p+n+1);
      	Big_int sumA,now,ans; 
      	sumA=p[0].a; 
      	for(int i=1;i<=n;i++){
      		now=sumA/p[i].b;
      		if(ans<now) ans=now;
      		sumA=sumA*p[i].a;
      	}
      	ans.Print();
      	return 0;
      }
      
    • Prob.3 开车旅行

      暴力的话是O(n2+nm),由于决策单一(即某人从某点出发到下一点这一过程是唯一确定的),可以进行倍增加速。
      由于是两人交替走,比一般的路径倍增要麻烦一点
      先借助set预处理出两个人分别从i号点向前走的下一个点是哪个以及走的距离。
      然后用to[i][j]表示从i号点出发,走2j轮(一轮为小A先走,小B再走)到达的目的地。用dis[i][j][0/1](0:小B,1:小A)与上面的to数组对应,即分别表示从i号点出发,走2j轮,小B/小A走过的距离和。
      这样通过倍增后,加速了答案的寻找过程,
         
      将时间复杂度优化为了O(n log2n+m log2n)
         
      更加详细的大佬题解
      -------------------------------------------------------->
         
      代码:

      #include<set> 
      #include<cstdio>
      #include<cstring>
      #include<iostream>
      #define INF 0x3f3f3f3f
      #define eps 0.000003
      #define MAXN 100005
      #define siter set<info>::iterator
      #define info(a,b) (info){a,b}
      using namespace std;//0 小B		1 小A 
      struct info{
      	int h,p;
      	bool operator <(const info rtm) const{
      		return h<rtm.h;
      	}
      };
      int to[MAXN][25],dis[MAXN][25][2],des[MAXN][2],len[MAXN][2],ans[2];
      int he[MAXN];
      int n,m,x,st;
      set<info>s;
      int sign(double i){
      	if(-eps<=i&&i<=eps) return 0;
      	if(i<-eps) return -1;
      	return 1;
      }
      int distant(int i,int j){
      	return he[i]>he[j]?he[i]-he[j]:he[j]-he[i];
      }
      void update(int i,info p){
      	int j=p.p,d=distant(i,j);
      	if(d<len[i][0]||(d==len[i][0]&&he[j]<he[des[i][0]])){
      		len[i][1]=len[i][0];des[i][1]=des[i][0];
      		len[i][0]=d;des[i][0]=j;	 
      	}
      	else if(d<len[i][1]||(d==len[i][1]&&he[j]<he[des[i][1]])){
      		len[i][1]=d;des[i][1]=j;
      	}
      }
      void drive(int i,int v){
      	for(int j=20;j>=0;j--) if(dis[i][j][0]+dis[i][j][1]<=v&&to[i][j]){
      		ans[0]+=dis[i][j][0];
      		ans[1]+=dis[i][j][1];
      		v=v-dis[i][j][0]-dis[i][j][1];
      		i=to[i][j];
      	}
      	if(len[i][1]<=v&&des[i][1]) ans[1]+=len[i][1];
      }
      int main()
      {
      	scanf("%d",&n);
      	for(int i=1;i<=n;i++) scanf("%d",&he[i]),len[i][0]=len[i][1]=INF;
      	siter si;
      	for(int i=n;i>=1;i--){
      		s.insert(info(he[i],i));
      		si=s.find(info(he[i],i));
      		si++;if(si!=s.end()){
      			update(i,*si);
      			si++; if(si!=s.end()) update(i,*si); si--;
      		}
      		si--;if(si!=s.begin()){
      			si--; update(i,*si);
      			if(si!=s.begin()) si--,update(i,*si);
      		}
      	}
      	for(int i=1;i<=n;i++) 
      		to[i][0]=des[des[i][1]][0],
      		dis[i][0][1]=len[i][1],
      		dis[i][0][0]=len[des[i][1]][0];
      	for(int j=1;j<=20;j++)
      		for(int i=1;i<=n;i++)
      			to[i][j]=to[to[i][j-1]][j-1],
      			dis[i][j][0]=dis[i][j-1][0]+dis[to[i][j-1]][j-1][0],
      			dis[i][j][1]=dis[i][j-1][1]+dis[to[i][j-1]][j-1][1];
      	scanf("%d",&x);
      	double rat=1e9; int ap=0;
      	for(int i=1;i<=n;i++){
      		ans[0]=ans[1]=0;
      		drive(i,x);
      		double tmp=ans[0]? 1.0*ans[1]/ans[0]:1e9;
      		if(sign(rat-tmp)==0&&he[i]>he[ap]) ap=i;
              if(sign(rat-tmp)>0) ap=i,rat=tmp;
      	}
      	printf("%d
      ",ap);
      	scanf("%d",&m);
      	for(int i=1;i<=m;i++){
      		ans[0]=ans[1]=0;
      		scanf("%d%d",&st,&x);
      		drive(st,x);
      		printf("%d %d
      ",ans[1],ans[0]);
      	}
      	return 0;
      }
      
    • Prob.4 同余方程

      (a,b互质辣)
      得出线性方程 ax+(-by)=1(其实不用加那个"-")
      拓展欧几里得求出一组解,然后把x调整到最小正整数。
      代码:

      #include<cstdio>
      #include<cstring>
      #include<iostream>
      using namespace std;
      void gcd(int a,int b,int &g,int &x,int &y){
      	if(!b){g=a; x=1; y=0;return;}
      	gcd(b,a%b,g,y,x); y-=x*(a/b);
      }
      int main(){
      	int a,b,x,y,g;
      	scanf("%d%d",&a,&b);
      	gcd(a,b,g,x,y);
      	b/=g;
      	if(x<0){int k=(0-x)/b+1;x+=k*b;}
      	if(x>0){int k=(x-0-1)/b;x-=k*b;}
      	printf("%d",x);
      	return 0;
      }


    • Prob.5 借教室

      1).线段树区间修改在线做,看什么时候区间最小值小于0即可。
      2).线段树常数大(但可以过的),可以二分+差分判断做
      代码:

      #include<cstdio>
      #include<cstring>
      #include<iostream>
      #define MAXN 1000006
      using namespace std;
      struct Application{
      	int d,l,r;
      }t[MAXN];
      int a[MAXN];
      int n,m;
      bool check(int p){
      	static int now,c[MAXN];
      	memset(c,0,sizeof(c)); now=0;
      	for(int i=1;i<=p;i++)
      		c[t[i].l-1]+=t[i].d,
      		c[t[i].r]-=t[i].d;
      	for(int i=0;i<=n;i++){
      		if(a[i]-now<0) return 0;
      		now+=c[i];
      	}
      	return 1;
      }
      int binary_search(){
      	int l=1,r=m,mid,now=0;
      	while(l<=r){
      		mid=(l+r)>>1;
      		if(check(mid)) now=mid,l=mid+1;
      		else r=mid-1;
      	}
      	return now;
      }
      int main()
      {
      	scanf("%d%d",&n,&m);
      	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
      	for(int i=1;i<=m;i++) scanf("%d%d%d",&t[i].d,&t[i].l,&t[i].r);
      	int ans=binary_search();
      	if(ans+1>n) printf("0");
      	else printf("-1
      %d",ans+1);
      	return 0;
      }
      


    • Prob.6 疫情控制

      贪心+二分+倍增

      二分时间,check操作,将所有军队按能否到达根节点分成两类:
      A类:无法在二分的时间内达到根节点。
      根据贪心策略,将这些军队移动到尽可能靠上的位置一定更优,所以把他们移动到他们所能到达的最靠近根的位置
      B类:在二分的时间内可以到达根节点。
      把他们放入一个数组,按到达根节点后剩余的时间从小到大排序。
      再对树跑一个dfs,维护出根的哪些儿子节点还需要一个B类军队去驻扎,把这些儿子节点放入另一个数组,按到根的时间从小到大排序。
      进行贪心,尝试用B类军队去覆盖没有还需要被驻扎的(根的儿子)节点:
      对于从小到大枚举到的某一个B类军队,首先判断他到根节点时进过的那个根的儿子节点是否被驻扎,若没有,则直接去驻扎那个节点。若已被驻扎,则尝试去驻扎从小到大枚举到的还需要被驻扎的第一个节点。(有一点绕,好好理解一下,正确性很容易证明)
      最后判断该时间下,那些还需要被驻扎的(根的儿子)节点是否被驻扎完。
      至于倍增用在哪里,显而易见,在将军队向上移动时,不可能一个一个地向father移动,所以倍增一下,加速移动过程。

      代码:

      洛谷和Vijos上过了,但Codevs和Tyvj上却WA了一个点,在Tyvj上把数据下了下来,手测却发现输出是正确的……

      不明原因,非常绝望,望有心人能解答疑难。

      #include<cstdio>
      #include<cstring>
      #include<iostream>
      #include<algorithm>
      #define ll long long
      #define MAXN 50005
      using namespace std;
      struct edge{
      	int to,next;
      	ll val;
      }e[MAXN*2];
      struct node{
      	int id; ll val;
      	bool operator<(const node &rtm) const{
      		return val<rtm.val;
      	}
      }ar[MAXN],ne[MAXN];
      ll stt[MAXN][20];
      int stu[MAXN][20];
      int p[MAXN],from[MAXN],head[MAXN];
      bool vis[MAXN];
      ll l,r,mid,ans;
      int n,m,ent=1,rs,cnt,nnt;
      void add(int u,int v,int w){
      	e[ent]=(edge){v,head[u],1ll*w};
      	head[u]=ent++;
      }
      void dfs(int u,int fa,ll dis,int fr){
      	if(fa==1) rs++;
      	stu[u][0]=fa;
      	stt[u][0]=dis;
      	if(fa==1) from[u]=u;
      	else from[u]=fr;
      	for(int j=1;j<=16;j++){
      		stu[u][j]=stu[stu[u][j-1]][j-1];
      		stt[u][j]=stt[u][j-1]+stt[stu[u][j-1]][j-1];
      	}
      	for(int i=head[u];i;i=e[i].next){
      		int v=e[i].to;
      		if(v==fa) continue;
      		if(u==1) dfs(v,u,e[i].val,v);
      		else dfs(v,u,e[i].val,fr);
      	}
      }
      void update(int u,int fa){
      	bool fg=1,fl=0;
      	for(int i=head[u];i;i=e[i].next){
      		int v=e[i].to;
      		if(v==fa) continue;
      		fl=1;
      		update(v,u);
      		if(!vis[v]) fg=0;
      		if(u==1&&!vis[v]) ne[++nnt]=(node){v,e[i].val};
      	}
      	if(fl) vis[u]=fg|vis[u];
      }
      bool check(ll x){
      	ll tmp;int u;
      	cnt=0; nnt=0;
      	memset(vis,0,sizeof(vis));vis[0]=1;
      	for(int i=1;i<=m;i++){
      		tmp=x; u=p[i];
      		for(int j=16;j>=0;j--)if(stu[u][j]&&tmp>=stt[u][j]){
      			tmp-=stt[u][j];
      			u=stu[u][j];
      		}
      		if(u==1) ar[++cnt]=(node){p[i],tmp};
      		else vis[u]=1;
      	}
      	update(1,0);
      	sort(ne+1,ne+nnt+1);
      	sort(ar+1,ar+cnt+1);
      	int pp=1,res=nnt;
      	for(int i=1;i<=cnt;i++){
      		while(vis[ne[pp].id]) pp++;
      		if(!vis[from[ar[i].id]]){
      			vis[from[ar[i].id]]=1;
      			res--;
      		}
      		else{
      			if(ar[i].val>=ne[pp].val){
      				vis[ne[pp].id]=1;
      				res--;
      			}
      		}
      		if(!res) return 1;
      	}
      	return 0;
      }
      void Binary(){
      	while(l<=r){
      		mid=(l+r)/2;
      		if(check(mid)) ans=mid,r=mid-1;
      		else l=mid+1;
      	}
      	printf("%lld",ans);
      } 
      int main()
      {
      	scanf("%d",&n);
      	l=1; r=0;
      	for(int i=1,a,b,c;i<n;i++){
      		scanf("%d%d%d",&a,&b,&c);
      		add(a,b,c); add(b,a,c);
      		r+=1ll*c;
      	}
      	dfs(1,0,0,0);
      	scanf("%d",&m);
      	for(int i=1;i<=m;i++)
      		scanf("%d",&p[i]);
      	if(m<rs) printf("-1
      ");
      	else Binary();
      	return 0;
      }
      


  • 相关阅读:
    java的泛型
    JAVA集合类--(一闪而过)
    java面向对象- -一闪而过
    进制转换之二进制与十进制转换
    跳跃表-原理及Java实现
    Excel二次开发相关代码
    程序员学炒股(7) 股市心得
    程序员学炒股(7) 股指期货收盘价对第二天开盘价有影响吗?
    程序员学炒股(6) 让我们来看一下7月份A股的表现
    程序员学炒股(5) 股指交割日效应是否存在?
  • 原文地址:https://www.cnblogs.com/zj75211/p/7811298.html
Copyright © 2011-2022 走看看