The 2018 ACM-ICPC China JiangSu (2018徐州邀请赛)(solve9/11)

题目链接:https://www.jisuanke.com/contest/1408?view=challenges

A题:

题意:就是给你n*m的格子,然后k个点,K个点有火,开始四个方向蔓延,问你最后烧的那个格子的位置,有多个的话,x优先再y优先。

思想:n*m*t暴力求即可。

#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int Map[2005][2005];
int main()
{
	int n,m;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		int k,a,b,Max=-1;
		scanf("%d",&k);
		memset(Map,INF,sizeof(Map));
		while(k--)
		{	
			scanf("%d%d",&a,&b);
			for(int i=1;i<=n;i++)
				for(int j=1;j<=m;j++)
					Map[i][j]=min(Map[i][j],abs(a-i)+abs(b-j));
		}
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				Max=max(Max,Map[i][j]);
		
		int flag=1;
		for(int i=1;i<=n && flag;i++)
		{
			for(int j=1;j<=m;j++)
			{
				if(Map[i][j]==Max)
				{
					printf("%d %d\n",i,j);
					flag=0;
					break;
				}
			}
		}	
	}
	return 0;
} 

B题

题意:让你求n的全排列中逆序对(i<j && a[i] > a[j] )的的个数为k的方案数。

思想:先打表看看是否有规律。下图借鉴一个打表的图,发现有规律。

æ表ç»æ

然后不难发现当j < i 的时候 dp[i][j]等于前边的dp[i-1][j]等i==j的时候发现d[i][j]=dp[i-1][j]-dp[i-1][j-i]

内存给了64M所以就需要滚动数组了。

#include<bits/stdc++.h>
using namespace std;
const long long mod = 1e9+7;
struct node{
	int n;
	int k;
	int id;
}no[5005];
bool cmp(node a,node b)
{
	return a.n<b.n;
}
long long dp[3][5005];
long long ans[5005];
int main()
{
	int cnt=0;
	int n,k,l=0;
	while(scanf("%d%d",&n,&k)!=EOF)
	{
		no[cnt].n=n;
		no[cnt].k=k;
		no[cnt].id=cnt;
		cnt++;
	}
	sort(no,no+cnt,cmp);
	dp[0][0]=1;
	for(int i=1;i<=5000;i++)
	{
		long long sum=0;
		for(int j=0;j<=5000;j++)
		{
			sum=(sum+dp[i-1&1][j])%mod;
			if(j>=i)
				sum=(sum-dp[i-1&1][j-i]+mod)%mod;
			dp[i&1][j]=sum;
		}
		while(l<cnt && no[l].n==i)
		{
			ans[no[l].id]=dp[i&1][no[l].k];
			l++;
		}
	}
	for(int i=0;i<cnt;i++)
		printf("%lld\n",ans[i]);
	return 0;
} 

D题

题意:题意:给定一个长度为n的向量(0,0,0..) 每次可以在某个位置加1,求加到最终向量(U1,U2,..,Un)的方案数。

思想: 组合数学问题记SUM=U1+U2..+Un 答案是SUM!/(U1!*U2!...*Un!) 预处理逆元 矩阵快速幂求解即可

E题

题意:求(1,2)到(n-1,m)和(2,1)到(n,m-1)不交叉的路径数。

思想: Lindstrm–Gessel–Viennotlemma 定理求解即可。 参考下:https://blog.csdn.net/passer__/article/details/81156093

G题

题意:有一个长度为n的序列,长度为m的划窗,计算每次划窗内的乘积的求和。

思想:分块,每一块为m,对于每一块都求出来前缀和和后缀和,对于当前区间恰好是这个块的话,那结果就等于后缀和,

如果不是的话,就等于下一块的那部分的前缀和*当前块的后缀和。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+5;
int n, m;
ll a[maxn], X, Y, Z, mod;
ll s[maxn], e[maxn];
int main()
{
    while(scanf("%d%d%lld", &n, &m, &mod) != EOF){
        scanf("%lld%lld%lld%lld", &a[0], &X, &Y, &Z);
        for(int i = 1; i < n; i++)
            a[i] = (X * a[i - 1] % mod * a[i - 1] % mod + Y * a[i - 1] % mod + Z) % mod;
         
        for(int i = 0; i < n; i++)
            if(i % m == 0)
                s[i] = a[i] % mod;
            else
                s[i] = s[i - 1] * a[i] % mod;
        for(int i = n - 1; i >= 0; i--)
            if((i + 1) % m == 0 || i == n - 1)
                e[i] = a[i] % mod;
            else
                e[i] = e[i + 1] * a[i] % mod;
         
        ll ans = 0;
        for(int i = 0; i <= n - m; i++)
            if(i % m == 0)
                ans += e[i];
            else
                ans = ans + (s[i + m - 1] * e[i]) % mod;
         
        printf("%lld\n", ans);
    }
 
    return 0;
}

H题

题意:计算N阶线性齐次递推式第K项。就是给你一个式子,让你求出来第K项。

思想:官方题解意思是模板·····模板·····众人所知的模板·····时间复杂度n^2*logk

https://github.com/ICPCCamp/BlackBoxLinearAlgebra  (出题人附送的PPT+代码)

此题也可以用fft写,时间复杂度为nlognlogk,BlackBoxLinearAlgebra算法的时间复杂度为n^2logk

I题

题意:n天有m见衣服,给你一个好感度为DP[i][j]为第今天穿第i件衣服明天穿第j件衣服的好感度,问你如何穿让好感度最高,输出好感度即可。

思想:假如考虑只有2天的话,是不是只需要两层for循环求一下最大值即可,三天的话三层,四天四层,因此发挥想象这就是一个n-1层for循环的一个式子,DP[i][j]这个矩阵的每个元素每次都取最大值。最后就变成第一天穿i这件衣服,最后一天穿j这件衣服的好感度的最大值。(没懂可以自己画画),这样就可以用矩阵快速幂来优化求解。

时间复杂度m^3logn

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct matrix{
    int row,col;
    ll A[105][105];
    matrix(int Row=0,int Col=0){  
        memset(A,0,sizeof(A));  
        row=Row;col=Col;  
    }  
};  
 
matrix operator *(matrix a,matrix b){  
    matrix c(a.row,b.col);  
    for(int i=0;i<a.row;i++)  
        for(int j=0;j<b.col;j++)  
            for(int k=0;k<a.col;k++)  
                c.A[i][j]=max(c.A[i][j],a.A[i][k]+b.A[k][j]);  
    return c;  
}  
matrix matrix_pow(matrix a,ll n){  
    matrix ans(a.row,a.col);  
    for(int i=0;i<a.row;i++)  
            ans.A[i][i]=0;  
    while(n){  
        if(n%2)  
            ans=a*ans;  
        a=a*a;  
        n/=2;  
    }  
    return ans;  
}  
int main()
{
    ll k;
    int m;
    while(scanf("%lld%d",&k,&m)!=EOF)
    {
        matrix temp(m,m);
        for(int i=0;i<m;i++)
            for(int j=0;j<m;j++)
                scanf("%lld",&temp.A[i][j]);
        temp=matrix_pow(temp,k-1);
        ll ans=0;
        for(int i=0;i<m;i++)
            for(int j=0;j<m;j++)
                ans=max(ans,temp.A[i][j]);
        printf("%lld\n",ans);
    }
    return 0;
} 

J题

题意:计算Meo set个数

思想:Meo set定义是1-n的子集中任何2个数的差值大于1,然后求集合元素内乘积的平方和。打表找规律。

结果是(n+1)!-1,java写一下就行。

K题

题意:给你一个n*n的矩阵代表是第i个城市到达每个城市都有一条路并且距离是a[i][j]在前提a[i][j]不等于0,如果等于0就说明没有路。问在不影响0到任何点的情况下删除最多的边,有多少种不同的方案。

思想:考虑只留下一个最小生成树即可,其余的边都删除,对于某点有多条路,那就删除C(n,n-1)条路,等价于删除C(n,1)。所以最后的结果就是所有点的度的一个乘积。

#include<bits/stdc++.h>
using namespace std;
long long mod = 1000000007;
long long Count[105];
int Map[105][105];
char str[105][105];
int head[105];
int dist[105];
int vis[105];
struct node{
    int v;
    int valu;
    int next;
}no[100*100*2];
int cnt,n;
void add(int u,int v,int valu)
{
    no[cnt].v=v;
    no[cnt].valu=valu;
    no[cnt].next=head[u];
    head[u]=cnt++;
}
void spfa(int u)
{
    for(int i=0;i<=n;i++)
    {
        vis[i]=0;
        dist[i]=1<<29;
    }
    vis[u]=1;
    dist[u]=0;
    queue<int>q;
    q.push(u);
    while(!q.empty())
    {
        int U=q.front();
        q.pop();
        vis[U]=0;
        for(int i=head[U];i!=-1;i=no[i].next)
        {
            int v=no[i].v;
            if(dist[v]>dist[U]+no[i].valu)
            {
                dist[v]=dist[U]+no[i].valu;
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
void diu(int u)
{
    memset(Count,0,sizeof(Count));
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(Map[i][j]==0)    
                continue;
            if(dist[j] == dist[i]+Map[i][j])
                Count[j]++;
        }
    } 
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        cnt=0;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++)
            scanf("%s",str[i]+1);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                Map[i][j]=str[i][j]-'0';
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(Map[i][j]!=0)
                    add(i,j,Map[i][j]);
        spfa(1);
        diu(1);
        long long ans=1;
        for(int i=2;i<=n;i++)
            ans=(ans*Count[i])%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章