• 【比赛】【2019.8.1】


    T1

    不是很难,注意积雪高度的判断(要开long long)以及终点不需要特判即可。

    #include<cstdio>
    #include<cstring>
    
    const int maxn=1e5+2;
    
    struct Solution
    {
    	struct Edge{int from,to,len;};
    	
    	struct Graph
    	{
    		Edge edges[maxn*10];
    		int edge_cnt,Head[maxn],Next[maxn*10];
    		
    		void add_edge(int from,int to,int len)
    		{
    			edges[++edge_cnt]=(Edge){from,to,len};
    			Next[edge_cnt]=Head[from],Head[from]=edge_cnt;return;
    		}
    	}G;
    	
    	int bh[maxn],lh[maxn],ad;
    	
    	long long dis[maxn],vis[maxn];
    	int S,T;
    	
    	int Q[maxn*10];
    	
    	void SPFA()
    	{
    		memset(dis,0x3f,sizeof(dis));
    		dis[S]=0;int h=0,t=0;Q[t++]=S;
    		
    		while( h!=t )
    		{
    			int p=Q[h++];h%=maxn*5;
    			vis[p]=0;
    			
    			for(int i=G.Head[p];i;i=G.Next[i])
    			{
    				Edge e=G.edges[i];
    				
    				if( ( e.to==T or bh[e.to]+ad*( (long long)dis[p]+e.len )<=lh[e.to] ) and dis[p]+(long long)e.len<dis[e.to] )
    				{
    					dis[e.to]=dis[p]+e.len;
    					if( !vis[e.to] ) vis[e.to]=1,Q[t++]=e.to,t%=maxn*5;
    				}
    			}
    		}
    		
    		return;
    	}
    	
    	void solve()
    	{
    		int n,m,tl;scanf("%d%d%d%d%d%d",&n,&m,&S,&T,&tl,&ad);
    		
    		for(int i=1;i<=n;i++) scanf("%d%d",&bh[i],&lh[i]);
    		
    		for(int i=1;i<=m;i++)
    		{
    			int x,y,l;scanf("%d%d%d",&x,&y,&l);
    			G.add_edge(x,y,l),G.add_edge(y,x,l);
    		}
    		
    		SPFA();
    		
    		if( dis[T]<=tl ) printf("%d",dis[T]);
    		else printf("wtnap wa kotori no oyatsu desu!");
    		
    		return;
    	}
    	
    }SOL;
    
    int main()
    {
    	SOL.solve();return 0;
    }
    

    T2

    这个有点意思。

    最短距离不难求,但是怎么求方案数呢?

    if( dis[s][i]+dis[i][t]==dis[s][t] ) ans[s][t]+=ans[s][i]*ans[i][t];
    

    上面的式子不难理解,但是实际题目中的写法必须是下面这样:

    for(int i=1;i<=n;i++)
        for(int s=1;s<=n;s++)
            for(int t=1;t<=n;t++)
                if( s!=i and i!=t and s!=t )
                {						
                    if( dis[s][i]+dis[i][t]<dis[s][t] )
                    {
                        dis[s][t]=dis[s][i]+dis[i][t];
                        ans[s][t]=ans[s][i]*ans[i][t];
                    }
                    else if( dis[s][i]+dis[i][t]==dis[s][t] ) ans[s][t]+=ans[s][i]*ans[i][t];
                }
    

    也就是说,我们要边求最短路边统计方案数

    为什么要这样呢?难道我不可以求完最短路再求方案数吗?

    不可以

    上面的例子,算完最短路之后,就会有两种“可行”的方案:

    • (1-2) (2-3-4)
    • (1-2-3) (3-4)

    问题在于:对于有多个节点的最短路径,我们要找到表示它的唯一方法

    这也就是为什么要在求最短路的时候同时求方案数。

    假设现在首先以(2)为中继点,那么我们就只能使(1 o 3)的方案数等于(1),至于(2 o 4)我们要不已经维护过了,要不就是维护不了的(因为不能把路径分解为(2 o 2)(2 o 4),图上没有自环,而且当前(2 o 4)还没有被维护到)

    然后,再枚举到以(3)为中继点的时候,就可以用(1 o 3)(3 o 4)来表示(1 o 4)这条路径了。

    (不用担心重复,重复的话意味着(1 o 2)(2 o 4)也被统计了,但是这里以(2)为中继点的时候,我们还没有维护(2 o 4)啊,而且维护完(2 o 4)的时候我们也不会再去枚举回(2)了,所以不会重复)

    扯了这么多,就是为了说明:要在求最短路的同时求方案数

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int maxn=102,maxm=9002;
    
    struct Solution
    {	
    	int dis[maxn][maxn];
    	long long ans[maxn][maxn];
    	
    	void solve()
    	{
    		memset(dis,0x3f,sizeof(dis));
    		int n,m;scanf("%d%d",&n,&m);
    	
    		for(int i=1;i<=m;i++)
    		{
    			int x,y,l;scanf("%d%d%d",&x,&y,&l);
    			
    			dis[x][y]=l,ans[x][y]=1;
    			dis[y][x]=l,ans[y][x]=1;
    		}
    		
    		for(int i=1;i<=n;i++)
    			for(int s=1;s<=n;s++)
    				for(int t=1;t<=n;t++)
    					if( s!=i and i!=t and s!=t )
    					{						
    						if( dis[s][i]+dis[i][t]<dis[s][t] )
    						{
    							dis[s][t]=dis[s][i]+dis[i][t];
    							ans[s][t]=ans[s][i]*ans[i][t];
    						}
    						else if( dis[s][i]+dis[i][t]==dis[s][t] ) ans[s][t]+=ans[s][i]*ans[i][t];
    					}
    		
    		for(int i=1;i<=n;i++)
    		{
    			double tmp=0.0;
    			
    			for(int s=1;s<=n;s++)
    			{
    				for(int t=1;t<=n;t++)
    				{
    					if( s==i or i==t or s==t ) continue;
    					if( dis[s][i]+dis[i][t]==dis[s][t] ) tmp+=( (double)ans[s][i]*ans[i][t] )/ans[s][t];
    				}
    			}
    			
    			printf("%.3lf
    ",tmp);
    		}
    		
    		return;
    	}
    	
    }SOL;
    
    int main()
    {
    	SOL.solve();return 0;
    }
    

    T3

    其实理解成矩阵就很好理解了:

    (A)矩阵和(B)矩阵分别表示经过(x)条边和(y)条边的矩阵,那么(C=A imes B)就是经过(x+y)条边的矩阵。

    JZ res;
    
    for(int i=1;i<=idx_cnt;i++)
        for(int s=1;s<=idx_cnt;s++)
            for(int t=1;t<=idx_cnt;t++)
                res.a[s][t]=std::min( res.a[s][t],a[s][i]+op.a[i][t] );
    
    return res;
    

    和普通的(Floyd)算法不同的是,在这里,等式两边的矩阵是相互独立的。也就是说,(C)矩阵中的(dis)不会对(A)(B)当中的产生影响,所以(C)矩阵是切切实实只表示(x+y)条边的矩阵。

    (Floyd)算法中,用(1)条边的信息统计完(2)条边的信息之后,(2)条边的信息又要马上在(dis)数组里面用来统计(3)条、(4)条边的信息,所以不是独立的。

    知道具体含义之后就可以矩阵快速幂了:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    int idx[1002],idx_cnt;
    
    struct JZ
    {
    	int a[105][105];
    	
    	JZ(){memset(a,0x3f,sizeof(a));}
    	
    	JZ operator * (const JZ &op)
    	{
    		JZ res;
    				
    		for(int i=1;i<=idx_cnt;i++)
    			for(int s=1;s<=idx_cnt;s++)
    				for(int t=1;t<=idx_cnt;t++)
    					res.a[s][t]=std::min( res.a[s][t],a[s][i]+op.a[i][t] );
    		
    		return res;
    	}
    	
    	JZ operator *= (const JZ &op)
    	{
    		*this = (*this)*op;
    		return *this;
    	}
    };
    
    int main()
    {
    	JZ dis;
    	
    	int K,m,S,T;scanf("%d%d%d%d",&K,&m,&S,&T);
    	
    	for(int i=1;i<=m;i++)
    	{
    		int len,x,y;scanf("%d%d%d",&len,&x,&y);
    		
    		if( !idx[x] ) idx[x]=++idx_cnt;
    		if( !idx[y] ) idx[y]=++idx_cnt;
    		
    		dis.a[idx[x]][idx[y]]=dis.a[idx[y]][idx[x]]=len;
    	}
    	
    	JZ ans=dis;--K;
    	
    	while(K)
    	{
    		if( K&1 ) ans*=dis;
    		dis*=dis;K>>=1;
    	}
    	
    	printf("%d",ans.a[idx[S]][idx[T]]);
    	return 0;
    }
    

    T4

    最短路+计算几何是真的神奇。

    首先,那些所在直线会穿过被保护节点(把它们分成两部分)的线段是不能建边的(要不就是真的穿过了,要不就是没有穿过,但是方向不对,选了之后会多绕一条线段)

    然后就要单向建边

    例如,如果对于(overrightarrow{AB}),所有被保护节点都在它的右边(也可以与它共线),那么就可以直接从(A)连一条边到(B)

    其实这样做是为了求有向图最小环(无向图的太难了,偷懒嘛)。

    有向图最小环就很简单,直接(Floyd)之后,(dis(i,i))就是经过(i)点的最小环的长度。

    无向图最小环另外讲。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    const int maxn=502;
    
    struct Solution
    {
    	struct Point
    	{
    		int x,y;
    		Point(int x=0,int y=0){this->x=x,this->y=y;}
    		
    		void read(){scanf("%d%d",&x,&y);return;}
    	};
    	typedef Point Vector;
    	
    	Vector make_vec(Point s,Point t){return Vector(t.x-s.x,t.y-s.y);}
    	int Cross(Vector A,Vector B){return A.x*B.y-A.y*B.x;}
    	
    	Point P1[maxn],P2[maxn];
    	
    	int dis[maxn][maxn];
    	
    	bool solve()
    	{
    		memset(dis,0x3f,sizeof(dis));
    		
    		int n1;if( scanf("%d",&n1)==EOF ) return 0;
    		for(int i=1;i<=n1;i++) P1[i].read();
    		
    		int n2;scanf("%d",&n2);
    		for(int i=1;i<=n2;i++) P2[i].read();
    		
    		for(int s=1;s<=n2;s++)
    			for(int t=1;t<=n2;t++)
    			{
    				if( s==t ) continue;
    				
    				bool flag=1;
    				
    				for(int k=1;k<=n1;k++)
    					if( Cross( make_vec( P2[s],P2[t] ),make_vec( P2[s],P1[k] ) )>0 ){flag=0;break;}
    				
    				if(flag) dis[s][t]=1;
    			}
    		
    		for(int i=1;i<=n2;i++)
    			for(int s=1;s<=n2;s++)
    			{
    				if( dis[s][i]==0x3f3f3f3f ) continue;
    				
    				for(int t=1;t<=n2;t++)
    					dis[s][t]=std::min( dis[s][t],dis[s][i]+dis[i][t] );
    			}
    		
    		int ans=0x3f3f3f3f;
    		for(int i=1;i<=n2;i++) ans=std::min( ans,dis[i][i] );;
    		
    		if( ans>n2 ) puts("ToT");
    		else printf("%d
    ",n2-ans);
    		
    		return 1;
    	}
    
    }SOL;
    
    int main()
    {
    	while( SOL.solve() );return 0;
    }
    
  • 相关阅读:
    博客第一篇:博客申请理由
    Cookie基础
    滚动篇————附一个简单单的自定义滚动条
    javascript中对字符串的操作总结
    javascript中创建对象的几种方式
    javascript中event汇总
    ...python scrapy
    Ubuntu 检测到系统出现问题 弹窗 嘿嘿
    万一哪天笔记全没了, 你真正记住的还有多少
    windows10安装mysql5.7.17是这样安装的吗?
  • 原文地址:https://www.cnblogs.com/info---tion/p/11287150.html
Copyright © 2020-2023  润新知