1. 程式人生 > 實用技巧 >十九、MySQL中DISTINCT與GROUP BY計數原理分析

十九、MySQL中DISTINCT與GROUP BY計數原理分析

P3953 逛公園

歡迎hack!畢竟這題被hack的人太多了,我也有可能出錯,希望大家帶著批判的眼光看這篇題解。

看完題就應該發現那個 \(k\) 特別小,顯然可以利用

關於 \(-1\) ,其實不是很好處理。非常顯然我們需要判斷的是“是否在一條滿足題意(可以從 \(1\)\(n\))的路徑上存在 \(0\) 環”,很多錯解都是因為對於“滿足題意的路徑”限制方法出錯然後掛掉的。

考慮對於每個節點開一個 \(dp[i][j]\) 表示:從這個點開始走,比最短路多走了 \(j\) 的長度的方案數

可以先一遍 \(Dijsktra\) 跑出從 \(1\) 到每個點的最短路 \(dis[i]\)

考慮從 \(1\) 開始記搜出 \(dp[1][i]\) 就是答案

其實原本是可以直接dp的,但是記搜過程可以順便判 \(0\) 環,不然還得再寫一個 \(dfs\) 判環。更重要的是,我們要找的是“可以到達的點上是否存在 \(0\) 環”,那麼在記搜過程中的體現就是訪問到了這個點,省去一些判斷,也可以少訪問一些節點。

  • \(0\) 環:對於這個狀態是否在棧中搜索開個 bool 記錄一下,如果訪問到狀態 \(i,j\) 並且 \(ins[i][j]\) 為真的情況那麼必然存在 \(0\) 環,因為它繞了一圈但是比最短路多的長度還是 \(j\) 。碰到 \(0\) 環直接退出即可

  • \(dp\)

    :考慮從 \(u\) 走到 \(v\) 會多產生多少貢獻。原本走到 \(v\)\(dis[v]\) 的長度,現在是 \(dis[u]+w[i]\) ,多走了 \(dis[u]+w[i]-dis[v]\) ,那麼還可以多走 \(lft-(dis[u]+w[i]-dis[v])\) ,即 \(dp[u][lft]\to dp[v][lft-(dis[u]+w[i]-dis[v])]\)

我就是這麼寫的,然而在洛谷被 hack 了,CCF的資料挺水的,就水過去了 所以這是我在考場上能AC的意思?

hack資料長這樣:

Input
1
4 5 0 10000
1 2 10
2 1 10
2 3 0
3 2 0
1 4 100
Output
1

像我那樣寫會被hack的原因在於我對於能到達的限制出現了問題,資料構造了一個死衚衕 \(1\to2\to3\) ,並且 \(2\to 3\to 2\)\(0\) 環,但是並不能到 \(n\)

接下去考慮解決這個漏洞。

發現只要在反圖上跑就能避免這個問題了,即從 \(n\) 跑到 \(1\) ,因為我們最短路是從 \(1\) 開始記錄的,如果在 \(n\)\(1\) 的路徑上出了個“岔子”或“死衚衕”,最短路一定不會往那裡延伸,那麼就能完美地解決這個問題了!

注意 dp 方程也要相應改變。

話說我這程式碼怎麼跑這麼快qwq,最優解第三,沒卡過常呢,正常寫的程式碼。

upd:卡完常變慢了/kk

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return f?x:-x;
}
#define N 100005
#define M 200005
#define K 55
int n,m,k,mod,ans,flg,dis[N],dp[N][K];
bool vis[N],ins[N][K];
void fmod(int&x){x-=mod,x+=x>>31&mod;}
struct edge{
	int nxt,to,val;
}e[M],E[M];
int head[N],num_edge,Head[N],Num_edge;
void addedge(int fr,int to,int val){
	++num_edge;
	e[num_edge].nxt=head[fr];
	e[num_edge].val=val;
	e[num_edge].to=to;
	head[fr]=num_edge;
}
void Addedge(int fr,int to,int val){
	++Num_edge;
	E[Num_edge].nxt=Head[fr];
	E[Num_edge].val=val;
	E[Num_edge].to=to;
	Head[fr]=Num_edge;
}
void clear(){
	ans=0;
	memset(dp,-1,sizeof(dp));
	memset(head,0,sizeof(head));
	memset(Head,0,sizeof(Head));
	num_edge=Num_edge=0,flg=1;
}
struct Dij{
	int u,dis;
	Dij(int u_,int d_){u=u_,dis=d_;}
	inline bool operator < (const Dij&t)const{return dis>t.dis;}
};
priority_queue<Dij>q;
void dij(){
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	q.push(Dij(1,dis[1]=0));
	while(!q.empty()){
		Dij now=q.top();q.pop();
		int u=now.u;
		if(vis[u])continue;
		vis[u]=1;
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(dis[v]>dis[u]+e[i].val){
				dis[v]=dis[u]+e[i].val;
				if(!vis[v])q.push(Dij(v,dis[v]));
			}
		}
	}
}
int dfs(int u,int lft){
	if(!flg)return 0;
	if(ins[u][lft])return flg=0;
	if(~dp[u][lft])return dp[u][lft];
	int res=0;
	ins[u][lft]=1;
	for(int i=Head[u];i&&flg;i=E[i].nxt){
		int v=E[i].to;
		int w=lft-(E[i].val-(dis[u]-dis[v]));
		if(w>=0)fmod(res+=dfs(v,w));
	}
	ins[u][lft]=0;
	if(u==1&&!lft)++res;
	return dp[u][lft]=res;
}
void Main(){
	clear();
	n=read(),m=read(),k=read(),mod=read();
	for(int i=1;i<=m;++i){
		int x=read(),y=read(),z=read();
		addedge(x,y,z),Addedge(y,x,z);
	}
	dij();
	for(int i=0;flg&&i<=k;++i)fmod(ans+=dfs(n,i));
	flg?printf("%d\n",ans):puts("-1");
}
signed main(){for(int T=read();T;--T)Main();}