zoukankan      html  css  js  c++  java
  • 【解题报告】NOIP2013

    【解题报告】NOIP2013

    Day1

    T1 转圈游戏

    思路

    我们手模发现,就是 \(x+m \times 10^k \space mod \space n\)

    然后一个快速幂就没了,开 long long

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    using namespace std;
    int n,m,k,x;
    long long ksm(long long a,long long b,int p)
    {
    	long long ans=1%p;
    	for( ;b;b>>=1)
    	{
    		if(b&1)
    		ans=(long long)ans*a%p;
    		a=(long long)a*a%p; 
    	}
    	return ans;
    }
    int main()
    {
    	cin>>n>>m>>k>>x;
    	cout<<(ksm(10,k,n)*m%n+x%n)%n<<endl;
    	return 0;
    }
    

    T2 火柴排队

    思路

    值域树状数组

    我们要使得 \(\sum (a_i-b_i)^2\) 最小

    变形一下

    \[\sum (a_i^2+2a_ib_i+b_i^2) \\= \sum (a_i^2+b_i^2)+\sum2a_ib_i \]

    我们发现 \(\sum (a_i^2+b_i^2)\) 是一个定值,所以我们只需要让 \(\sum 2a_ib_i\) 最小就好了

    如何让两个数列的 \(2a_ib_i\) 最小呢

    我们可以让两个序列中的数字各自标上排名

    然后如果两个数列的数字的排名相对应,我们就可以让它最小

    比如说

    2 3 1 4

    3 2 1 4

    我们只需要交换一次

    再比如

    1 3 4 2

    1 7 2 4

    我们让他们对应,也就是将第二个序列中的 7 2 4 变成 4 7 2 ,交换两次

    那我们就以第一个数列为基准,对于这个数列我们就可以离散化一下,表示排名,然后建立两个数列间的类似于最长公共子序列的 \(O(n^2)\) 的做法的方法来建立一一映射的关系

    值域树状数组求逆序对即可

    这样就做完了

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    using namespace std;
    const int maxn=500005;
    const int mod=99999997;
    int tree[maxn],rank[maxn],n;
    long long ans;
    struct point{
    	int num,val;
    }a[maxn],b[maxn];
    int lowbit(int x)
    {
    	return x&(-x);
    }
    bool cmp(point q,point w)
    {
    	if(q.val==w.val)
    	return q.num<w.num;
    	return q.val<w.val;
    }
    void add(int x,int y)
    {
    	for( ;x<=n;x+=lowbit(x)) tree[x]+=y,tree[x]%=mod;
    }
    int ask(int x)
    {
    	int ans=0;
    	for( ;x;x-=lowbit(x)) ans+=tree[x],ans%=mod;
    	return ans;
    }
    int main()
    {
    	std::ios::sync_with_stdio(false);
    	cin>>n;
    	for(int i=1;i<=n;i++)
    	{
    		cin>>a[i].val;
    		a[i].num=i;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		cin>>b[i].val;
    		b[i].num=i;
    	}
    	sort(a+1,a+1+n,cmp);
    	sort(b+1,b+1+n,cmp);
    	for(int i=1;i<=n;i++)
    	rank[a[i].num]=b[i].num;
    	for(int i=1;i<=n;i++)
    	{
    		add(rank[i],1);
    		ans+=(i-ask(rank[i]));
    		ans%=mod;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

    T3 货车运输

    思路

    最大生成树 + LCA

    我们要求的是每一条路径中最小边的最大值,这个最大值肯定会加入最大生成树,所以我们做一个最大生成树

    然后,对于这棵树,我们倍增预处理每一条边到自己的 \(k\) 级祖先的答案,然后我们用倍增 \(lca\) 来合并答案

    然后就没了

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #include <cmath> 
    using namespace std; 
    
    const int maxn=10005;
    const int INF=999999999;
    
    struct edge1{  
        int x,y,dis;
    }ed1[50005]; 
    struct edge2{
        int to,next,w;
    }ed2[100005];
    int cnt,n,m,head[maxn],deep[maxn],f[maxn],fa[maxn][21],w[maxn][21];
    bool vis[maxn]; 
    void add_edge(int from, int to, int w)
    {
        ed2[++cnt].next=head[from];
        ed2[cnt].to=to;
        ed2[cnt].w=w;
        head[from]=cnt;
        return ;
    }
    
    bool cmp(edge1 x, edge1 y)
    {
        return x.dis>y.dis; //将边权从大到小排序 
    }
    
    int find(int x)
    {
        if(f[x]!=x) f[x]=find(f[x]);
        return f[x];
    }
    
    void kruskal()
    {
        sort(ed1+1,ed1+m+1,cmp); 
        for(int i=1;i<=n;i++)
        f[i]=i;
        for(int i=1; i<=m; i++)
        if(find(ed1[i].x)!=find(ed1[i].y))
    	{
            f[find(ed1[i].x)]=find(ed1[i].y);
            add_edge(ed1[i].x, ed1[i].y, ed1[i].dis);
            add_edge(ed1[i].y, ed1[i].x, ed1[i].dis); 
        }
        return ;
    }
    
    void dfs(int node)
    {
        vis[node]=true;
        for(int i=head[node]; i; i=ed2[i].next)
    	{
            int to=ed2[i].to;
            if(vis[to]) continue;
            deep[to]=deep[node]+1;
            fa[to][0]=node;
            w[to][0]=ed2[i].w;
            dfs(to);
        }
        return ;
    }
    
    int lca(int x, int y)
    {
        if(find(x)!=find(y)) return -1;
        int ans=INF;
        if(deep[x]>deep[y]) swap(x,y);
        for(int i=20; i>=0; i--)
            if(deep[fa[y][i]]>=deep[x])
    		{
                ans=min(ans, w[y][i]);
                y=fa[y][i];
            }
        if(x==y) return ans;
        for(int i=20; i>=0; i--)
            if(fa[x][i]!=fa[y][i])
    		{
                ans=min(ans, min(w[x][i], w[y][i]));
                x=fa[x][i]; 
                y=fa[y][i];
            }
        ans=min(ans, min(w[x][0], w[y][0]));
        return ans;
    }
    
    int main()
    {
        int x,y,z,q;
        cin>>n>>m;
        for(int i=1; i<=m; i++)
    	{
            cin>>x>>y>>z;
            ed1[i].x=x;
            ed1[i].y=y;
            ed1[i].dis=z;
        }
        kruskal();
        for(int i=1;i<=n;i++)
            if(!vis[i])
    		{
                deep[i]=1; 
                dfs(i);
                fa[i][0]=i;
                w[i][0]=INF;
            }
        for(int i=1;i<=20;i++)
            for(int j=1;j<=n;j++)
    		{
                fa[j][i]=fa[fa[j][i-1]][i-1]; 
                w[j][i]=min(w[j][i-1], w[fa[j][i-1]][i-1]);
            }
        cin>>q;
        for(int i=1; i<=q; i++)
    	{
    		cin>>x>>y;
            cout<<lca(x,y)<<endl;
        }
        return 0;
    } 
    

    Day2

    T1 积木大赛

    思路

    贪心

    每往后走一个,想着把前面所有的填到跟他一个高度,这样就是最小填的次数

    别忘了加最开始的第一堆积木的高度

    #include<bits/stdc++.h>
    using namespace std;
    int n,a[100005];
    long long ans=0;
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)     
        cin>>a[i];
        for(int i=2;i<=n;i++)     
        if(a[i]>a[i-1]) 
        ans+=a[i]-a[i-1];
        cout<<ans+a[1];
        return 0;
    }
    

    T2 花匠

    思路

    真的贪心,假的DP

    求一个最长波浪形子序列

    贪心策略是,每个连续上升或者下降的子序列中,只有一个能选,所以我们直接统计一下波浪的数量,然后加上第一个就好了

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    using namespace std;
    int n;
    int fro,pro=0;
    int ans;
    int main()
    {
    	cin>>n;
    	cin>>fro;
    	for(int i=2;i<=n;i++)
    	{
    		int x;
    		cin>>x;
    		if(x==fro)
    		continue;
    		if(pro!=1&&x>fro)//上升 
    		{
    			ans++;
    			pro=1;
    		}
    		else if(pro!=2&&x<fro)
    		{
    			ans++;
    			pro=2;
    		}
    		fro=x;
    	}
    	ans++;
    	cout<<ans<<'\n';
    	return 0;
    }
    

    T3 华容道

    BFS搜索加暴力巨恶心卡常

    但说实话,正解我不会

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #include <queue>
    #include <deque>
    using namespace std;
    const int maxn=35;
    inline int read()
    {
    	int x=0,f=1;
    	char c=getchar();
    	while(c<'0'||c>'9')
    	{
    		if(c=='-') f=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9')
    	{
    		x=(x<<1)+(x<<3)+c-'0';
    		c=getchar();
    	}
    	return x*f;
    }
    inline void write(int x)
    {
    	if(x<0) putchar('-'),x=-x;
    	if(x>9) write(x/10);
    	putchar(x%10+'0');
    }
    struct node{
    	int x,y,ddx,ddy,st;
    };
    int n,m,T;
    int a[maxn][maxn];
    bool vis[maxn][maxn][maxn][maxn];
    int dx[]={0,0,1,0,-1};
    int dy[]={0,1,0,-1,0};
    
    
    int main()
    {
    	std::ios::sync_with_stdio(false);
    	cin.tie(0),cout.tie(0);
    	cin>>n>>m>>T;
    	for(register int i=1;i<=n;i++)
    	{
    		for(register int j=1;j<=m;j++)
    		cin>>a[i][j];
    	}
    	while(T--)
    	{
    		deque <node> q;
    		bool flag=false;
    		memset(vis,false,sizeof(vis));
    		int ex,ey,sx,sy,tx,ty;
    		cin>>ex>>ey>>sx>>sy>>tx>>ty;
    
    		node now;
    		now=(node){sx,sy,ex,ey,0};
    		vis[ex][ey][sx][sy]=true;
    		q.push_back(now);
    		while(q.size())
    		{
    			now=q.front();
    			q.pop_front();
    			int x=now.x;
    			int y=now.y;
    			int lx=now.ddx;
    			int ly=now.ddy;
    			if(x==tx&&y==ty) 
    			{
    				flag=true;
    				cout<<now.st<<'\n';
    				break;
    			}
    			
    			int fx=lx+0,fy=ly+1;
    			int vx,vy;
    			if(fx<n||fy<m||fx>1||fy>1)
    			{
    				vx=x,vy=y;
    				if(x==fx&&y==fy)
    				vx=lx,vy=ly;
    				if(a[fx][fy]&&!vis[fx][fy][vx][vy])
    				{
    					vis[fx][fy][vx][vy]=true;
    					node nt;
    					nt=(node){vx,vy,fx,fy,now.st+1};
    					q.push_back(nt);
    				}
    			}
    			
    			fx=lx+1,fy=ly+0;
    			if(fx<n||fy<m||fx>1||fy>1)
    			{
    				vx=x,vy=y;
    				if(x==fx&&y==fy)
    				vx=lx,vy=ly;
    				if(a[fx][fy]&&!vis[fx][fy][vx][vy])
    				{
    					vis[fx][fy][vx][vy]=true;
    					node nt;
    					nt=(node){vx,vy,fx,fy,now.st+1};
    					q.push_back(nt);
    				}
    			}
    			
    			fx=lx+0,fy=ly-1;
    			if(fx<n||fy<m||fx>1||fy>1)
    			{
    				vx=x,vy=y;
    				if(x==fx&&y==fy)
    				vx=lx,vy=ly;
    				if(a[fx][fy]&&!vis[fx][fy][vx][vy])
    				{
    					vis[fx][fy][vx][vy]=true;
    					node nt;
    					nt=(node){vx,vy,fx,fy,now.st+1};
    					q.push_back(nt);
    				}
    			}
    			
    			fx=lx-1,fy=ly+0;
    			if(fx<n||fy<m||fx>1||fy>1)
    			{
    				vx=x,vy=y;
    				if(x==fx&&y==fy)
    				vx=lx,vy=ly;
    				if(a[fx][fy]&&!vis[fx][fy][vx][vy])
    				{
    					vis[fx][fy][vx][vy]=true;
    					node nt;
    					nt=(node){vx,vy,fx,fy,now.st+1};
    					q.push_back(nt);
    				}
    			}
    		}
    		if(!flag) 
    		cout<<-1<<'\n';
    	}
    	return 0;
    }
    
    本博文为wweiyi原创,若想转载请联系作者,qq:2844938982
  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    JAVA后端开发规范
    IntelliJ IDEA配置
  • 原文地址:https://www.cnblogs.com/wweiyi2004/p/15542563.html
Copyright © 2011-2022 走看看