题目链接: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;
}