正睿培訓 8.4 學習筆記
8.4 學習筆記
模擬賽,題目比較水,資料也比較水。
T1
題目大意,每一個點有一個點權,每一條邊有一個邊權,我們強制必須走某個點和某一條邊,對所有的點和邊,求 \(1\) 到 \(n\) 的最短路。
直接從 \(1\) 和 \(n\) 分別跑一遍最短路即可。
程式碼:
#include<bits/stdc++.h> #define dd double #define ld long double #define ll long long #define uint unsigned int #define ull unsigned long long #define N 100010 #define M 400020 using namespace std; const int INF=0x3f3f3f3f; template<typename T> inline void read(T &x) { x=0; int f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c == '-') f=-f; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; x*=f; } struct edge{ int from,to,next,w; inline void intt(int fr_,int to_,int ne_,int w_){ from=fr_;to=to_;next=ne_;w=w_; } }; edge li[M]; int head[N],tail; inline void add(int from,int to,int w){ li[++tail].intt(from,to,head[from],w); head[from]=tail; } int n,m; struct node{ int id,val; inline node(){} inline node(int id,int val) : id(id),val(val) {} inline bool operator < (const node &b) const{ return val>b.val; } }; priority_queue<node> q; int d1[N],d2[N]; bool vis[N]; inline void dij(){ memset(d1,INF,sizeof(d1)); memset(d2,INF,sizeof(d2)); d1[1]=0;d2[n]=0; q.push(node(1,0)); while(q.size()){ node top=q.top();q.pop(); if(vis[top.id]) continue; vis[top.id]=1; for(int x=head[top.id];x;x=li[x].next){ int to=li[x].to,w=li[x].w; if(vis[to]||d1[to]<d1[top.id]+w) continue; d1[to]=d1[top.id]+w;q.push(node(to,d1[to])); } } q.push(node(n,0)); memset(vis,0,sizeof(vis)); while(q.size()){ node top=q.top();q.pop(); if(vis[top.id]) continue; vis[top.id]=1; for(int x=head[top.id];x;x=li[x].next){ int to=li[x].to,w=li[x].w; if(vis[to]||d2[to]<d2[top.id]+w) continue; d2[to]=d2[top.id]+w;q.push(node(to,d2[to])); } } } int main(){ // freopen("my.in","r",stdin); // freopen("my.out","w",stdout); read(n);read(m); for(int i=1;i<=m;i++){ int from,to,w;read(from);read(to);read(w); add(from,to,w);add(to,from,w); } dij(); // for(int i=1;i<=n;i++){ // printf("i:%d d1:%d d2:%d\n",i,d1[i],d2[i]); // } for(int i=1;i<=n;i++){ printf("%d\n",d1[i]+d2[i]); } for(int i=1;i<=2*m;i+=2){ printf("%d\n",min(d1[li[i].from]+d2[li[i].to],d1[li[i].to]+d2[li[i].from])+li[i].w); } return 0; }
T2
給定一個圖,圖有障礙,有若干起點,有一些空格。從每個點開始走,一步可以一直走,走到沒有障礙為止(當然也可以中途停下)問每個點到起點的最短路是多少(走到最近的最短路就可以)。
水題。\(n^2m\) 也能過。
我們直接用多源 bfs ,從每個節點開始進行 bfs ,只要是空地,不管有沒有人經過,都衝進去,這樣可以卡成 \(O(n^2m)\) ,但是因為資料水,所以沒卡。一個更好的做法是記錄一下每一個方向有沒有人之前走過,所以我們可以記錄一下每一個方向有沒有走過,利用 bfs 最短路的正確性,如果之前有人走過,那麼現在走一定不優。
所以這個題就可以過了。
程式碼:
#include <bits/stdc++.h> using namespace std; const int OutputBufferSize = 10000000; namespace input { #define BUF_SIZE 100000 #define OUT_SIZE 100000 #define ll long long bool IOerror = 0; inline char nc() { static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE; if (p1 == pend) { p1 = buf; pend = buf + fread(buf, 1, BUF_SIZE, stdin); if (pend == p1) { IOerror = 1; return -1; } } return *p1++; } inline bool blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; } inline void read(char &ch) { ch = nc(); while (blank(ch)) ch = nc(); } inline void read(int &x) { char ch = nc(); x = 0; for (; blank(ch); ch = nc()); if (IOerror) return; for (; ch >= '0' && ch <= '9'; ch = nc()) x = x * 10 + ch - '0'; } #undef ll #undef OUT_SIZE #undef BUF_SIZE }; namespace output { char buffer[OutputBufferSize]; char *s = buffer; inline void flush() { assert(stdout != NULL); fwrite(buffer, 1, s - buffer, stdout); s = buffer; fflush(stdout); } inline void print(const char ch) { if (s - buffer > OutputBufferSize - 2) flush(); *s++ = ch; } inline void print(char* str) { while (*str != 0) print(char(*str++)); } inline void print(int x) { char buf[25] = {0}, *p = buf; if (x == 0) print('0'); while (x) *(++p) = x % 10, x /= 10; while (p != buf) print(char(*(p--) + '0')); } } const int maxn = 2005; const int inf = 0x3f3f3f3f; char s[maxn][maxn]; int n, m, a[maxn][maxn]; struct node{ int x,y; inline node(){} inline node(int x,int y) : x(x),y(y) {} }; queue<node> q; inline int check(int x,int y){ if(s[x][y]=='#') return 0; if(a[x][y]!=inf) return -1; return 1; } inline void bfs(){ while(q.size()){ // printf("1\n"); node top=q.front();q.pop(); for(int i=1;i+top.x<=n;i++){ int now=check(i+top.x,top.y); if(now==0) break; else if(now==-1) continue; a[i+top.x][top.y]=a[top.x][top.y]+1; q.push(node(i+top.x,top.y)); } for(int i=1;top.x-i>=1;i++){ int now=check(top.x-i,top.y); if(now==0) break; else if(now==-1) continue; a[top.x-i][top.y]=a[top.x][top.y]+1; q.push(node(top.x-i,top.y)); } for(int i=1;i+top.y<=m;i++){ int now=check(top.x,i+top.y); if(now==0) break; else if(now==-1) continue; a[top.x][i+top.y]=a[top.x][top.y]+1; q.push(node(top.x,i+top.y)); } for(int i=1;top.y-i>=1;i++){ int now=check(top.x,top.y-i); if(now==0) break; else if(now==-1) continue; a[top.x][top.y-i]=a[top.x][top.y]+1; q.push(node(top.x,top.y-i)); } } } int main() { // freopen("my.in","r",stdin); // freopen("my.out","w",stdout); input::read(n), input::read(m); // scanf("%d %d\n",&n,&m); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { input::read(s[i][j]); } // scanf("%s",s[i]+1); } memset(a,inf,sizeof(a)); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(s[i][j]=='+'){ q.push(node(i,j));a[i][j]=0; } else if(s[i][j]=='#') a[i][j]=-1; } bfs(); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (a[i][j] == -1) { output::print('#'); } else if (a[i][j] == inf) { output::print('X'); } else { output::print(a[i][j]); } output::print(" \n"[j == m]); } } output::flush(); return 0; }
T3
這是一道數論,因為自己沒有運用組合數太長時間,所以忘卻,這提醒我數學還是要多看多想。
題意簡述:
給定一個 \(n\times m\) 的網格,左上角是 \((0,0)\) ,右下角是 \(n-1,m-1\) ,其中滿足 \(x>y\) 的點 \((x,y)\) 都是危險點,不能走,並且,另給了 \(k\) 個危險點,也不能走,問從 \((0,0)\) 走到 \((n-1,m-1)\) 的方案數是多少。
其中,模數是:\(999911659=2\times 3\times 4679\times 35617+1\) 。設方案數為 \(\alpha\) ,則我們的答案是 \(233^\alpha\)
首先,我們考慮 \(k=0\) ,利用卡特蘭數的方法去做,不難得出一個組合數相減的式子。
這道題中 \(n,m\le 10^9\) 而 \(k\) 只有 \(1000\) 。我們考慮容斥,首先把所有障礙點排序,讓到達第 \(i\) 個障礙點時,所以可能經過的障礙點都在 \(i\) 的前面。
設 \(f_i\) 表示從 \((0,0)\) 走到第 \(i\) 個障礙點且不經過其他障礙點的方案數,設 \(g_{(x_0,y_0),(x_1,y_1)}\) 表示從 \((x_0,y_0)\) 走到 \((x_1,y_1)\) 不經過 \(x>y\) 的點的方案數是多少。
所以就有:
\[f_i=g_{(0,0),(i_x,i_y)}-\sum\limits_{j_x\le i_x,j_y\le i_y} f_j\times g_{(j_x,j_y),(i_x,i_y)} \]考慮把所有到達第 \(i\) 個障礙點的方案按照有沒有經過障礙和經過的第一個障礙點是多少來劃分,上面的轉移明顯正確,\(g\) 可以通過之前類似於卡特拉數的計算得出。
最後,我們需要組合數取模,根據費馬小定理,我們需要給方案數模 \(999911658\) ,但是顯然這個東西是一個合數,於是我們根據上面的分解式對每一個素數做一遍 dp ,然後用孫子定理合並就可以了。
程式碼:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define int long long
#define uint unsigned int
#define ull unsigned long long
#define N 1010
#define M 40000
using namespace std;
const int INF=0x3f3f3f3f;
const int base=232;
const int P=999911659;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
int n,m,k,f[N],mod;
inline int ksm(int a,int b,int mod){
int res=1;
while(b){
if(b&1) res=1ll*res*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return res;
}
inline int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
int jie[M],inv[M],invjie[M],ans;
const int p[5]={0,2,3,4679,35617};
struct point{
int x,y;
inline point(){}
inline point(int x,int y) : x(x),y(y) {}
inline bool operator < (const point &b) const{
if(x!=b.x) return x<b.x;
return y<b.y;
}
};
point a[N];
inline void Prework(){
jie[0]=1;inv[0]=1;invjie[0]=1;jie[1]=1;inv[1]=1;invjie[1]=1;
for(int i=2;i<=mod-1;i++){
jie[i]=1ll*jie[i-1]*i%mod;inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
invjie[i]=1ll*invjie[i-1]*inv[i]%mod;
}
}
inline int C(int n,int m){
if(n<m) return 0;
return 1ll*jie[n]*invjie[m]%mod*invjie[n-m]%mod;
}
inline int Lucas(int n,int m){
if(n<m) return 0;
if(n<mod) return C(n,m);
return Lucas(n/mod,m/mod)*Lucas(n%mod,m%mod)%mod;
}
inline int calc(point a,point b){
if(b.x<=a.y) return Lucas(b.x-a.x+b.y-a.y,b.x-a.x);
return (Lucas(b.x-a.x+b.y-a.y,b.x-a.x)-Lucas(b.x-a.y+b.y-a.x,b.x-a.y-1)+mod)%mod;
}
inline int solve(){
Prework();
if(k==0){
return (calc(point(0,0),point(n-1,m-1))%mod+mod)%mod;
}
f[1]=calc(point(0,0),a[1]);
int nowans=1ll*calc(a[1],point(n-1,m-1))*f[1]%mod;
for(int i=2;i<=k;i++){
for(int j=1;j<=i-1;j++){
if(!(a[j].x<=a[i].x&&a[j].y<=a[i].y)) continue;
f[i]=(f[i]+1ll*f[j]*calc(a[j],a[i])%mod)%mod;
}
f[i]=(calc(point(0,0),a[i])-f[i]+mod)%mod;
nowans=(nowans+1ll*calc(a[i],point(n-1,m-1))*f[i]%mod);
}
for(int i=1;i<=k;i++) f[i]=0;
return ((calc(point(0,0),point(n-1,m-1))-nowans)%mod+mod)%mod;
}
signed main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);read(m);read(k);
for(int i=1;i<=k;i++){
read(a[i].x);read(a[i].y);
}
sort(a+1,a+k+1);
for(int i=1;i<=4;i++){
mod=p[i];int now=(P-1)/mod;
int s=solve();
// printf("s:%lld\n",s);
ans=(ans+1ll*now*ksm(now,mod-2,mod)%(P-1)*s%(P-1))%(P-1);
}
// printf("ans:%lld\n",ans);
printf("%lld\n",ksm(233,ans,P));
return 0;
}