T1 並查集/二分圖染色
T2 DP+矩陣乘法優化
T3 數位DP
100+100+30=230 rank13
想出了T2矩陣乘法好開森,然並卵……
%sqj wsj zhz大佬AK怒噴水題
後附zhz大佬的蔑視
看題解或代碼的看目錄,有索引。
T1
問題 A: 七天使的通訊
時間限制: 2 Sec 內存限制: 256 MB
題目描述
n個天使排成一條直線,某些天使之間需要互相聯繫,他們之間的通訊可以通過黑白兩種通道中的一種;所有通道必須在直線同側(另一側是地面);爲了保證通訊效率,同種顏色的所有通道之間不能相交。請計算能否建立這種通訊方案。
輸入
第一行一個數T,表示接下來有T個詢問。
對於每個詢問:第一行兩個數n,m,分別表示有n個天使、需要建立通訊線路的天使有m對;接下來有m行,每行兩個數a、b,表示a、b兩個天使需要通訊。
輸出
對於每個詢問,輸出一行“sane”表示有可行方案、“non”表示無解
樣例輸入
1
7 5
1 3
2 7
3 4
7 4
6 5
樣例輸出
sane
提示
【樣例解釋】
樣例中共有一個詢問。
在(1,3)、(4,7)、(5,6)之間連黑色通道,在(2,7)、(3,4)之間連白色通道,每條通道都成功建立,且同種顏色的通道沒有相交,所以輸出sane。
【數據規模和約定】
對於 20%的數據,1<=n<=50,1<=m<=15
對於 50%的數據,1<=n<=1000,1<=m<=300
對於 100%的數據,1<=n<=5000,1<=m<=1000,1<=T<=10,1<=a<=n,1<=b<=n
數據保證每對(a,b)不重複,且a不等於b
【提示】
當兩條線路有一對相同的端點時,這兩條線路不相交。
也就是說,對於線路(a,b)和線路(c,d)(a
Solution
將每個通道設爲一個節點,先暴力判斷每兩條通道如果是同種顏色會不會相交,如果會相交就在這兩個節點之間連無向邊,說明它們不能爲同種顏色(必須在二分圖兩邊)。然後對組成的無向圖進行二分圖判定(DFS染色),如果染色成功說明該圖是一個二分圖,即有解,否則無解。
PS:爲什麼在我眼中顯然的並查集竟然是二分圖……
Code
並查集
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1010;
int a[MAXN],b[MAXN];
int fa[MAXN],val[MAXN],rank[MAXN];
int n,m;
inline bool pd(int a,int b,int c,int d)
{
return (a<c&&c<b&&b<d || c<a&&a<d&&d<b);
}
int getfa(int x)
{
if (fa[x]==x) return fa[x];
int y=fa[x];
fa[x]=getfa(fa[x]);
val[x]=(val[x]+val[y])&1;
return fa[x];
}
bool solve()
{
for (int i=1;i<=m;i++) fa[i]=i,val[i]=0,rank[i]=0;
for (int i=1;i<m;i++)
for (int j=i+1;j<=m;j++)
if (pd(a[i],b[i],a[j],b[j]))
{
int x=i,y=j;
int fx=getfa(x),fy=getfa(y);
if (fx!=fy)
{
if (rank[fx]<rank[fy])
{
fa[fx]=fy;
val[fx]=(val[y]+1-val[x])&1;
}
else
{
fa[fy]=fx;
val[fy]=(val[x]+1-val[y])&1;
rank[fx]++;
}
}
else if (val[x]==val[y]) return false;
}
return true;
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&a[i],&b[i]);
if (a[i]>b[i]) swap(a[i],b[i]);
}
if (solve()) printf("sane\n");
else printf("non\n");
}
//system("pause");
return 0;
}
二分圖染色
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1010;
int a[MAXN],b[MAXN],color[MAXN];
int Head[MAXN],Next[MAXN*MAXN],To[MAXN*MAXN];
bool vis[MAXN];
int n,m,tot;
inline bool pd(int a,int b,int c,int d)
{
return (a<c&&c<b&&b<d || c<a&&a<d&&d<b);
}
void add(int x,int y)
{
tot++;
Next[tot]=Head[x];
Head[x]=tot;
To[tot]=y;
}
bool dfs(int u)
{
if (color[u]==-1) color[u]=1;
vis[u]=true;
for (int i=Head[u];i;i=Next[i])
{
int v=To[i];
if (vis[v] && color[v]==color[u]) return false;
if (!vis[v]) color[v]=1^color[u];
if (!vis[v] && !dfs(v)) return false;
}
return true;
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
tot=0;
memset(Head,0,sizeof(Head));
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&a[i],&b[i]);
if (a[i]>b[i]) swap(a[i],b[i]);
}
for (int i=1;i<=m;i++)
for (int j=i+1;j<=m;j++)
if (pd(a[i],b[i],a[j],b[j])) add(i,j),add(j,i);
bool flag=false;
memset(color,-1,sizeof(color));
memset(vis,false,sizeof(vis));
for (int i=1;i<=m;i++)
if (!vis[i] && !dfs(i)) {printf("non\n"); flag=true; break;}
if (!flag) printf("sane\n");
}
return 0;
}
T2
問題 B: 都市環遊
時間限制: 1 Sec 內存限制: 512 MB
題目描述
因爲SJY乾的奇怪事情過多,SJY收到了休假的通知,於是他準備在都市間來回旅遊。SJY有一輛車子,一開始行駛性能爲0,每過1時間行駛性能就會提升1點。每個城市的道路都有性能要求。SJY一共有t時間休息,一開始他位於1號城市(保證1號城市道路要求爲0),他希望在n號城市結束旅程。每次穿過一條城市間的路會花費1時間,當然他也可以停留在一個城市不動而花費1時間。當且僅當車子的行駛性能大於等於一個城市,我們才能到達那裏。SJY希望知道,旅遊的方案模10086後的答案。(只要在某一時刻通過的道路存在一條不相同,就算不同的方案)
輸入
第一行三個數n,m,t,表示有n個城市m條道路t時間。
第二行n個數,hi表示第i個城市的道路性能要求。
第三到m+2行,每行兩個數u,v,表示城市u與城市v之間有一條單向道路連接(可能有重邊)。
輸出
包括一個數字,表示旅遊的方案模10086。
樣例輸入
5 17 7
0 2 4 5 3
1 2
2 1
1 3
3 1
1 4
4 1
4 5
5 4
5 3
4 1
2 1
5 3
2 1
2 1
1 2
2 1
1 3
樣例輸出
245
提示
【數據規模和約定】
對於20%的數據,n<=10,t<=80;
對於50%的數據,n<=30,t<=80;
對於100%的數據,n<=70,m<=1000,t<=100000000,hi<=70。
Solution
算法1:
暴力 ,搜索全部路徑,發現每一步的行動可以用一個轉移矩陣表示,每一步相當於乘上矩陣。
算法2:
由算法1得到,將每一步移動的轉移矩陣都求出來,然後進行矩陣乘法,時間複雜度 O(n^3*t)
算法 3:
在算法2的基礎上利用矩陣快速冪優化矩陣乘法,時間複雜度O(n^3*logt)
PS:爲什麼標解中每一步都是矩陣乘法啊,我覺得應該是DP,設f[i][j]表示到i城市j時間的方案數,在j>=t時f[i][j]+=f[k][j-1] 有一條k—>j的路徑,然後纔想到矩陣乘法優化的……莫非是我太菜了?
Code
#include<bits/stdc++.h>
using namespace std;
#define MOD 10086
const int MAXN=110;
int f[MAXN][MAXN],a[MAXN][MAXN],h[MAXN];
int n,m,T,max_h;
struct matrix
{
int a[MAXN][MAXN];
int row,col;
matrix(int n=0) //Identity matrix;
{
row=col=n;
memset(a,0,sizeof(a));
for (int i=1;i<=n;i++) a[i][i]=1;
}
friend matrix operator * (const matrix &a,const matrix &b)
{
if (a.col!=b.row) {cout<<"error!";}
matrix c;
c.row=a.row; c.col=b.col;
for (int i=1;i<=a.row;i++)
for (int j=1;j<=b.col;j++)
{
c.a[i][j]=0;
for (int k=1;k<=a.col;k++)
{
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%MOD;
//if (c.a[i][j]>=MOD) c.a[i][j]%=MOD;
}
}
return c;
}
}A,F;
void pre()
{
scanf("%d%d%d",&n,&m,&T);
for (int i=1;i<=n;i++)
scanf("%d",&h[i]);
for (int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
a[x][y]++;
}
max_h=-1;
for (int i=1;i<=n;i++) a[i][i]=1,max_h=max(max_h,h[i]);
}
matrix power(matrix a,int b)
{
matrix ans=matrix(n);
while (b)
{
if (b&1) ans=ans*a;
a=a*a;
b>>=1;
}
return ans;
}
void solve()
{
f[1][0]=1;
for (int j=1;j<=min(T,max_h);j++)
{
for (int i=1;i<=n;i++)
{
f[i][j]=0;
if (j>=h[i])
for (int k=1;k<=n;k++)
f[i][j]=(f[i][j]+f[k][j-1]*a[k][i])%MOD;
}
}
if (T<=max_h) {printf("%d",f[n][T]); return;}
// the rest use matrix mul;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
A.a[i][j]=a[i][j];
A.row=A.col=n;
for (int i=1;i<=n;i++)
F.a[1][i]=f[i][max_h];
F.row=1; F.col=n;
A=power(A,T-max_h);
F=F*A;
cout<<F.a[1][n];
}
int main()
{
pre();
solve();
//system("pause");
return 0;
}
T3
問題 C: 大水題
時間限制: 1 Sec 內存限制: 512 MB
題目描述
dzy 定義一個n^2 位的數的生成矩陣A 爲一個大小爲n*n 且Aij 爲這個數的第i*n+j-n位的矩陣。
現在dzy 有一個數n^2 位的數k,他想知道所有小於等於k 的數的n*n 生成矩陣有多少種。(如果不足n^2 位則補前綴零)
輸入
第一行一個數n,第二行一個n^2 位的數k
輸出
僅一行表示答案,答案可能很大,你只需輸出答案對10^9 + 7 取模後的結果。
樣例輸入
2
1000
樣例輸出
954
提示
【數據規模和約定】
對於30% 的數據n<=2
對於100% 的數據n <=1000,且n爲偶數
【提示】
如果兩個生成矩陣在其中一個旋轉180 度後可以重疊,則稱這兩個矩陣是相同的。
Solution
這題其實我也還沒動,先上標稱吧,
PS:zhz大佬的蔑視
Code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int x[1000010],mo=1000000007,n,f[1000010][2][2];
char ch[1000011];
int solve(){
memset(f,0x00,sizeof f); f[0][1][0]=1;
for (int i=1;i<=n*n;i++){
for (int k=0;k<=9;k++)
if (k<x[n*n-i+1])
f[i][0][0]=(f[i][0][0]+f[i-1][0][0]+f[i-1][0][1])%mo;
else if (k>x[n*n-i+1])
f[i][0][1]=(f[i][0][1]+f[i-1][0][0]+f[i-1][0][1])%mo;
else {
f[i][0][0]=(f[i][0][0]+f[i-1][0][0])%mo;
f[i][0][1]=(f[i][0][1]+f[i-1][0][1])%mo;
}
for (int k=0;k<x[i];k++)
if (k<x[n*n-i+1])
f[i][0][0]=(f[i][0][0]+f[i-1][1][0]+f[i-1][1][1])%mo;
else if (k>x[n*n-i+1])
f[i][0][1]=(f[i][0][1]+f[i-1][1][0]+f[i-1][1][1])%mo;
else {
f[i][0][0]=(f[i][0][0]+f[i-1][1][0])%mo;
f[i][0][1]=(f[i][0][1]+f[i-1][1][1])%mo;
}
if (x[i]<x[n*n-i+1]) f[i][1][0]=(f[i-1][1][0]+f[i-1][1][1])%mo;
else if (x[i]>x[n*n-i+1]) f[i][1][1]=(f[i-1][1][0]+f[i-1][1][1])%mo;
else{
f[i][1][0]=f[i-1][1][0]; f[i][1][1]=f[i-1][1][1];
}
}
f[n*n][0][0]--;
return (f[n*n][0][0]+f[n*n][1][0])%mo;
}
int solve2(){
int ans=0;
for (int i=1;i<=n*n/2;i++) ans=(ans*10+x[i])%mo;
for (int i=n*n/2+1;i<=n*n;i++) if (x[n*n-i+1]<x[i]) return ans; else if (x[n*n-i+1]>x[i]) return (ans-1+mo)%mo;
return ans;
}
int main(){
freopen("water.in","r",stdin);
freopen("water.out","w",stdout);
scanf("%d",&n); scanf("%s",ch+1);
for (int i=1;i<=n*n;i++) x[i]=ch[i]-'0';
int ans=0;
for (int i=1;i<=n*n;i++) ans=(ans*10+ch[i]-'0')%mo;
int k1=(1ll*(solve()-solve2()+mo)*500000004%mo+mo)%mo;
ans=((ans-k1)%mo+mo)%mo;
cout<<ans<<endl; return 0;
}