題目鏈接: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;
}