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
",i,d1[i],d2[i]);
// }
for(int i=1;i<=n;i++){
printf("%d
",d1[i]+d2[i]);
}
for(int i=1;i<=2*m;i+=2){
printf("%d
",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 == '
' || ch == '
' || ch == ' ';
}
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
");
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,&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("
"[j == m]);
}
}
output::flush();
return 0;
}
T3
这是一道数论,因为自己没有运用组合数太长时间,所以忘却,这提醒我数学还是要多看多想。
题意简述:
给定一个 (n imes m) 的网格,左上角是 ((0,0)) ,右下角是 (n-1,m-1) ,其中满足 (x>y) 的点 ((x,y)) 都是危险点,不能走,并且,另给了 (k) 个危险点,也不能走,问从 ((0,0)) 走到 ((n-1,m-1)) 的方案数是多少。
其中,模数是:(999911659=2 imes 3 imes 4679 imes 35617+1) 。设方案数为 (alpha) ,则我们的答案是 (233^alpha)
首先,我们考虑 (k=0) ,利用卡特兰数的方法去做,不难得出一个组合数相减的式子。
这道题中 (n,mle 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) 的点的方案数是多少。
所以就有:
考虑把所有到达第 (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
",s);
ans=(ans+1ll*now*ksm(now,mod-2,mod)%(P-1)*s%(P-1))%(P-1);
}
// printf("ans:%lld
",ans);
printf("%lld
",ksm(233,ans,P));
return 0;
}