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;
      }
      


  • 相关阅读:
    Zabbix5 Frame 嵌套
    Zabbix5 对接 SAML 协议 SSO
    CentOS7 安装 Nexus
    CentOS7 安装 SonarQube
    GitLab 后台修改用户密码
    GitLab 查看版本号
    GitLab Admin Area 500 Error
    Linux 安装 PostgreSQL
    Liger ui grid 参数
    vue.js 是一个怪东西
  • 原文地址:https://www.cnblogs.com/zj75211/p/7811298.html
Copyright © 2011-2022 走看看