noip2013 简要题解

2 篇文章 0 订阅

day1  P1

ans = (x + m *(10^k )) %n   10^k %n 用快速幂计算即可

#include <iostream>
using namespace std;
long long n,m,k,x;
long long power(long long x, long long y, long long n){
     long long ans = 1;
     while (y){
           if(y & 1) ans = ( ans * x) % n;
           y/=2;  x = (x * x) %n;
     }
     return ans;
}
int main()
{
    cin >> n >> m >> k >> x;
    cout << (x + m * ( power( 10 , k , n)) % n) %n << endl;

    return 0;
}

 

day1 P2

(ai-bi)^2 = ai*ai +bi*bi-2ai*bi  最小化结果 显然就是最大化  ai *bi的和,因此我们要找每个ai对应的bi是谁,显然假设ai< aj  那么一定满粗  bi< bj

因为不难证明 如果bi>bj 交换对应关系能使得值更优

那么显然最后问题变成了如何移动b使得移动次数最少,显然根据a我们可以知道每个b最终所在的位置。假设最终所在位置是实际的大小(假设那样是对b的有序化),那么

这个问题就可以转变为求逆序对的问题,就是在那种相对大小的情况下,当前b数组的逆序对。

#include <iostream>
#include <algorithm>
using namespace std;
const int mod = 99999997;
const int maxn = 1000000+10;
struct Node{
       int v,pos;
       bool operator<(const Node & o)const{
            return v < o.v;
       }
}a[maxn],b[maxn];
int c[maxn],id[maxn];
void insert(int x){
     for (; x<maxn; x+=(x & (-x))) c[x]++;
}
int  getSum(int x){
     int ans = 0;
     for (; x; x-=(x & (-x))) ans+=c[x];
     return ans;
}
int main()
{
    int n;
    cin>>n;
    for (int i = 0; i < n; ++i){
        cin >> a[i].v; a[i].pos = i+1;
    }
    for (int i = 0; i < n; ++i){
        cin >> b[i].v; b[i].pos = i+1;
    }
    sort(a,a+n);
    sort(b,b+n);
    for (int i = 0; i <n; ++i){
        id[a[i].pos] = b[i].pos;
    }
    int ans = 0;
    for (int i = n; i>=1; --i){
        ans = (ans+ getSum(id[i])) % mod;
        insert(id[i]);
    }
    cout << ans << endl;
    return 0;
}



day1 P3

直接求出最大生成树,然后询问两个节点路径上的最小值即可。求路径上的最小值,因为树是静态的,所有可以求LCA的时候顺便把路上最小值求出来。

可以用倍增算法求,比较简便

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int INF = 0x7fffffff;
const int maxn = 10000+10;
const int maxm = 50000+10;
struct Edge{
       int x,y,w;
       bool operator<(const Edge & o)const{
            return w > o.w;
       }
}e[maxm];
vector<int> v[maxn];
int f[maxn][20],L[maxn][20],father[maxn],dep[maxn];
bool vis[maxn];
int n,m,q,x,y,ans;
void  addEdge(int x,int y, int w){
      v[x].push_back(y); 
      v[x].push_back(w);
      v[y].push_back(x);
      v[y].push_back(w);
}
int  find(int x){
     if (father[x]==0) return x;
     return (father[x] = find( father[x]) );
}
void dfs(int x, int fa){
     vis[x] = true;
     int len = v[x].size();
     for (int i = 0; i < len; i+=2){
         int y = v[x][i];
         int w = v[x][i+1];
         if (y!=fa){
             dep[y] = dep[x] + 1;
             L[y][0] = w;
             f[y][0] = x;
             dfs(y,x);
         }
     }
}
void  getMaxPath(){
      for (int j = 1; j < 16; ++j)
          for (int i = 1; i <= n; ++i){
              f[i][j] = f[f[i][j-1]][j-1];
              L[i][j] = min(L[i][j-1], L[f[i][j-1]][j-1]);
          }
}
int   getAns(int x, int y){
      if (dep[x] < dep[y]) swap(x,y);
      int delta = dep[x] - dep[y];
      for (int i = 15; i >=0; i--)
          if (delta & (1<<i)) {
              ans = min(L[x][i],ans);
              x = f[x][i];
          }  
      if (x==y) return x;
      for (int i = 15; i >=0; i--)
          if (f[x][i] != f[y][i]){
             ans = min(ans,L[x][i]);
             ans = min(ans,L[y][i]);
             x = f[x][i];
             y = f[y][i];
          }
      ans = min(ans,L[x][0]);
      ans = min(ans,L[y][0]);
      return f[x][0];
}
int main()
{
    cin>> n >> m;
    for (int i =  0 ; i< m; ++i){
        scanf("%d %d %d", &e[i].x,&e[i].y,&e[i].w);
    }
    sort(e,e+m);
    for (int i = 0; i < m;++i){
        int x = find(e[i].x);
        int y = find(e[i].y);
        if (x!=y){
              addEdge(e[i].x,e[i].y,e[i].w);
              father[x] = y;
        }
    }
    memset(father,0,sizeof(father));
    for (int i = 1; i <= n; ++i) 
        for (int j = 0; j <16; ++j) L[i][j] = INF;
    memset(vis, 0, sizeof(vis));
    for (int i = 1; i <= n; ++i) 
    if (!vis[i]){
        dep[i] = 0;
        dfs(i,0);
    }
    getMaxPath();
    cin >> q;
    for (int i = 1; i <= q; ++i){
        scanf("%d %d",&x, &y);
        ans = INF;
        if (find(x)!=find(y)) {
           int lca = getAns(x,y);
           if (lca == 0) ans = -1;
        }
        printf("%d\n",ans);
    }
    return 0;
}



day2  P1

一个贪心即可,显然 如果h[i]>h[i-1] 那么 h[i]-h[i-1] 是不得不花新的代价来建造的。否则h[i] 不超过h[i-1]部分可以在h[i-1]部分完成的时候也顺便解决

#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
    int n, h ;
    cin >> n;
    long long ans = 0;
    int  last = 0;
    for (int i = 0; i < n ; ++i){
        scanf("%d", &h);
        if ( h > last) ans += h-last;
        last = h;
    }
    cout << ans << endl;
    return 0;
}


 

day2 P2

 f[i][1] = max(f[j][0])+1 a[j]<a[i];
 f[i][0] = max(f[j][1])+1 a[j]>a[i];

显然类似于LIS问题, 用树状数组或者线段树优化到nlogn 之后想了一下 貌似只要扫一边计算 拐角点的个数就可以了。

#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
const int maxm = 1000000+10;
const int maxn = 100000+10;
int c0[maxm],c1[maxm], h[maxn];
int n;
void insert(int *c, int x,int val){
     for (;x<maxm; x+=x&(-x)) c[x] = max(c[x],val);
}
int getMax(int *c, int x){
    if (!x) return 0;
    int ans = 0;
    for (; x; x-=x&(-x)) ans = max(ans,c[x]);
    return ans;
}
int main()
{
    cin>> n ;
    int ans = 1;
    for (int i = 1; i <= n ; ++i){
        scanf("%d", &h[i]); h[i]++;
    }
    insert(c0,h[1],1);
    insert(c1,maxm-1-h[1],1);
    for (int i = 2; i <= n; ++i){
        int tmp = getMax(c0,h[i]-1);
        ans = max(ans,tmp+1);
        insert(c1,maxm-1-h[i],tmp+1);
        
        tmp = getMax(c1,maxm-1-h[i] -1);
        ans = max(ans,tmp+1);
        insert(c0,h[i],tmp+1);
    }
    cout << ans << endl;
    return 0;
}


day2 P3

一道很经典的广搜题 ,需要优化,就是空格一旦确定的情况下,那么其他1的所有位置也确定,并且目标位置的一定必须要把空格先移动过去,

那么这个空格的移动的最少次数我们可以先通过bfs预处理出来。

这样复杂度就是预处理n^4 + qn^2

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
using namespace std;
const int inf = 0x7fffffff;
const int maxn = 30+5;
const int  dir[4][2]={0,-1,0,1,-1,0,1,0};
int mat[maxn][maxn], dis[maxn][maxn][4], step[maxn][maxn][4][4];
int d[maxn][maxn];
int n,m,q,test,ex,ey,sx,sy,tx,ty;
struct node{
       int x,y;
};
struct node2{
       int x,y,k;
};
bool	inside(int x, int y){
	return (x>=1 && x <= n && y>= 1 && y <= m);
}
int spfa(){
	queue<node2> q;
	while (!q.empty()) q.pop();
	for (int k = 0; k < 4; ++k)
		if (dis[sx][sy][k]!=inf)
			q.push((node2){sx,sy,k});
	while (!q.empty()){	
		int x = q.front().x;
		int y = q.front().y;
		int k = q.front().k;
		//cout <<x<<" "<<y<<" "<<k<<" "<<dis[x][y][k]<<endl;
		q.pop();
		for (int i = 0;  i < 4; ++i){
			int _x = x + dir[i][0];
			int _y = y + dir[i][1];
			if (inside(_x,_y))
			if (mat[_x][_y])
			if (step[x][y][k][i]!=inf)
			if (dis[_x][_y][i ^ 1] > dis[x][y][k]+step[x][y][k][i] +1) {
				dis[_x][_y][i ^ 1] = dis[x][y][k]+step[x][y][k][i] +1;
				q.push((node2){_x,_y,i ^ 1 });
			}
		}
	}
	int ans = inf;
	for (int i = 0; i < 4; ++i)
		if (dis[tx][ty][i] < ans) ans = dis[tx][ty][i];
	return (ans==inf?-1:ans);
}
int bfs(int sx, int sy, int tx, int ty){
	if (!mat[sx][sy]) return inf;
	if (!mat[tx][ty]) return inf;
	for (int i = 1; i <= n; ++i)
		for (int  j = 1; j <=m; ++j) d[i][j] = inf;
	d[sx][sy] = 0;
	queue<node> q;
	while (!q.empty()) q.pop();
	q.push((node){sx,sy});
	while (! q.empty()){
		if (d[tx][ty] != inf) return d[tx][ty];
		int x = q.front().x;
		int y = q.front().y;
		q.pop();
		for (int i = 0; i < 4; ++i){
			int _x = x+dir[i][0];
			int _y = y+dir[i][1];
			if (inside(_x,_y))
			if (mat[_x][_y] && d[_x][_y]==inf){
				d[_x][_y] = d[x][y]+1;
				q.push((node){_x,_y});
			}
		}
	}
	
	return inf;
}
void init(){
	for (int i = 1; i <= n; ++i )
		for (int j = 1; j <= m ; ++j){
			int v = mat[i][j];
			mat[i][j] = 0;
			for (int k = 0; k < 4; ++k)
				for (int l = 0; l < 4; ++l)
					step[i][j][k][l] = bfs(i+dir[k][0],j+dir[k][1],
                    i+dir[l][0],j+dir[l][1]);
			mat[i][j] = v;
		}
}	
int 	getAns(){
	scanf("%d %d %d %d %d %d", &ex,&ey,&sx,&sy,&tx,&ty);
	if (sx ==tx && sy== ty) return 0;
	if (sx==ex && sy==ey) return -1;
	if (!inside(ex,ey) || !inside(sx,sy) || !inside(tx,ty)) return -1;
	if (!mat[ex][ey] || !mat[sx][sy] || !mat[tx][ty]) return -1;
	for (int i = 1; i <= n; ++i)
		for (int j =1 ; j <= m; ++j)
			for (int k = 0; k < 4; ++k)
					dis[i][j][k] = inf;
	mat[sx][sy] = 0;
	for (int k = 0; k < 4; ++k){
		 dis[sx][sy][k] = bfs(ex,ey,sx+dir[k][0],sy+dir[k][1]);
         //cout << k << " "<<dis[sx][sy][k]<<endl;	
    }
	mat[sx][sy] = 1;

	return spfa(); 
}
int main()
{
    cin >> n >> m >> test;
    for (int i = 1;  i <= n; ++i)
        for (int j = 1; j <= m ;++ j)
            cin>> mat[i][j];
    init();
    while (test--)
		printf("%d\n",getAns());
		
    return 0;
}


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值