萌新妹子刚学OI 55pts 求助(带注释)
查看原帖
萌新妹子刚学OI 55pts 求助(带注释)
227728
冰糖鸽子TJ鸽子协会楼主2021/7/14 14:15

RT,对了Sub 1246,错了七个点(8/整个Sub5/25/28/29)

思路是先处理每条边,求出其新的值(每个质因数次数膜k后的)和唯一一个与其相乘合法的值;然后拓扑一下,反着边判合法边DP,最后取最大值。

代码如下:


// Problem: P6381 『MdOI R2』Odyssey
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P6381
// Memory Limit: 500 MB
// Time Limit: 1000 ms
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;
#define M 200005//数据范围
#define O 10//质因数个数
// #define int long long //加这个会出奇怪的错
const int Li=350;//找质数时的限制,比sqrt(1e5)大
struct node//一条边的数据,包含边的基础信息,长度分解的质因数后的,以及思路里提到的两个值
{
	int s,t,L,W,ww,cnt,ct[O],num[O];
	//s=u,t=v,L=l,W和ww即思路里的那两个,前者是这条边的,后者是需要的
	//cnt是质因数个数,num[i]是质因数,ct[i]是每个num[i]次数,这些TopoDP的时候用不到了
}rd[M],fc;//最多有8个质因数,不慌
vector<int>road[M];//存路,链式前向行不回
queue<int>q;//拓扑用
map<int,int>f[M];//DP用,定义下面DP函数写了
int n,m,kp,P[Li],ans,Pcnt,vis[M],topo[M],out[M],in[M];//topo存顺序
//nmkp是nmk,P是质数,Pcnt先是质数个数后面用来拓扑,vis,out,in都如其名
bool CHPR(int x)//CHeck PRime
{
	for(int i=2;i*i<=x;i++) if(x%i==0) return 0;
	return 1;
}
void YCLP() {for(int i=2;i<=Li;i++) if(CHPR(i)) P[++Pcnt]=i;}
//Yu Chu Li Prime
void Dod(int u,int v,int w,int l,int bh)//处理每条边
{
	ans=max(ans,l),rd[bh].s=u,rd[bh].W=rd[bh].ww=1,rd[bh].t=v,rd[bh].L=l,out[u]++,in[v]++,road[u].push_back(bh);int tsj,asd=w;
	//ans这里是因为"一条边也是完美路径"
	//W和ww是1,因为后面要乘其他的,tsj记录去0后多少质因数,asd调试的时候用的,可以删
	for(int i=1;P[i]*P[i]<=w;i++)
	{
		if(w%P[i]==0) rd[bh].num[++rd[bh].cnt]=P[i];//先存下这个数
		while(w%P[i]==0) rd[bh].ct[rd[bh].cnt]++,w/=P[i];//记录次数
		rd[bh].ct[rd[bh].cnt]%=kp;//取模
	}
	if(w!=1) rd[bh].ct[++rd[bh].cnt]=(1%kp),rd[bh].num[rd[bh].cnt]=w; tsj=rd[bh].cnt;
	for(int i=1;i<=rd[bh].cnt;i++) if(!rd[bh].ct[i]&&tsj--) for(int j=i+1;j<=rd[bh].cnt;j++) rd[bh].ct[j-1]=rd[bh].ct[j],rd[bh].num[j-1]=rd[bh].num[j]; rd[bh].cnt=tsj;//如果还剩一个质因数,因为我们处理质因数只处理到了sqrt(1e5)左右
	for(int i=1;i<=rd[bh].cnt;i++) 	rd[bh].W*=pow(rd[bh].num[i],rd[bh].ct[i]),rd[bh].ww*=pow(rd[bh].num[i],(kp-(rd[bh].ct[i])));//求两个值
	// cout<<endl<<rd[bh].W<<' '<<rd[bh].ww<<endl;
	// printf("第%d条边,数据:\n u=%d,v=%d,Len=%d,w=%d,质因数个数是%d,而质因数分别是:\n",bh,u,v,l,asd,rd[bh].cnt);
	// for(int i=1;i<=rd[bh].cnt;i++) printf("%d^%d  ",rd[bh].num[i],rd[bh].ct[i]); puts("");
	//以上调试用
}
void Topo()//正常的拓扑
{
	int up;//存队首
	for(int i=1;i<=n;i++) if(!in[i]) q.push(i),topo[++Pcnt]=i;//先把没有入边的怼进去
	while(!q.empty())
	{
		up=q.front(),q.pop();
		for(int i=0;i<road[up].size();i++)
		{
			fc=rd[road[up][i]];//为了方便
			in[fc.t]--;if(!in[fc.t]) q.push(fc.t),topo[++Pcnt]=fc.t;//如果删了这个点没有入边了就怼进去
		}
	}
}
void DP()//正常的DP
{
	//设f[i][j]表示若上一条边的w是j,走到的i开始的最长路,第二维用map
	reverse(topo+1,topo+1+n);//要反着枚举
	for(int cc=1,u=topo[cc],v;cc<=n;cc++,u=topo[cc])//u表示现在是哪个点
	{
		if(!out[u]) continue;//没有v就不用处理了,其实这条加不加区别不大
		for(int i=0;i<road[u].size();i++)
		{
			fc=rd[road[u][i]],v=fc.t;
			f[u][fc.ww]=max(f[u][fc.ww],fc.L+f[v][fc.W]);//因为是fc.W/ww,所以一定是合法的,换句话说如果v中间没有这个值的边那f[v][w]一定是0
			ans=max(ans,f[u][fc.ww]);//取最大值,每个点都可能是最长完美路径的开头
			// printf("f[%d][%d] now is %d\n",u,fc.ww,f[u][fc.ww]); //调试用
		}
	}
	cout<<ans<<endl;
}
signed main()
{
	cin>>n>>m>>kp,YCLP();//预处理
	for(int i=1,u,v,w,l;i<=m;i++) cin>>u>>v>>w>>l,Dod(u,v,w,l,i);//处理每条边
	Pcnt=0,Topo(),DP();//拓扑DP,输出在DP里
	return 0;
}
2021/7/14 14:15
加载中...