ARC090简要题解

鸽子又回来了!(震声
在这里插入图片描述
提前四分钟跑路(
C题:
简单DP一发就完事。RE是因为,我访问到了DP[3][?],但是我的dp数组第一维大小是3。wszz。

#include<bits/stdc++.h>
using namespace std;
int n;
int dp[300][1010];
int val[300][1010];
int main() { 
	scanf("%d", &n);
	for(int i=1;i<=2;++i) { 
		for(int j=1;j<=n;++j) { 
			scanf("%d", &val[i][j]);
		} 
	} 
	memset(dp, -0x3f, sizeof(dp));
	dp[1][1] = val[1][1];
	for(int i=1;i<=2;++i) { 
		for(int j=1;j<=n;++j) { 
			dp[i][j+1] = max(dp[i][j+1], dp[i][j] + val[i][j+1]);
			dp[i+1][j] = max(dp[i+1][j], dp[i][j] + val[i+1][j]);
		} 
	} 
	cout<<dp[2][n];
	return 0;
}  

D题:
本来想写带权并查集的,然后发觉不太会。想了想之后发现,他好像只需要判个是否合法,并不需要输出在第几个挂的。那么我们直接建图连边,然后随便抓个点往下dfs就完事。
具体的,用 ( u , v , l e n ) (u, v, len) (u,v,len) ( v , u , − l e n ) (v,u,-len) (v,u,len)来描述 v a l [ u ] − v a l [ v ] = l e n val[u]-val[v]=len val[u]val[v]=len这件事,然后如果合法,这图里头两点间的所有路径的长度就应该是个定值。抓个点dfs下去,对于每个边,如果他的目的地被dfs过了,那么判一下是否满足关系。

#include<bits/stdc++.h>
using namespace std;
struct edge { 
	int to, len;
};

vector<edge>ed[100100];
bool vis[100010];
long long dist[100010];
int n, m;
bool OK = 1;

inline void ade(int l, int r, int val) { 
	ed[l].push_back({r, val});
	ed[r].push_back({l, -val});
} 

void dfs(int nw, long long len) { 
	vis[nw] = 1, dist[nw] = len;
	for(int i=0;i<ed[nw].size();++i) { 
		int tar = ed[nw][i].to, nlen = ed[nw][i].len;
		if(!vis[tar]) 
			dfs(tar, len + nlen);
		else
			if(dist[tar] != len + nlen) 
				OK = 0;
	} 
	return;
} 

int main() { 
	scanf("%d%d", &n, &m);
	for(int i=1;i<=m;++i) { 
		int l, r, val;
		scanf("%d%d%d", &l, &r, &val);
		ade(l, r, val);
	} 
	for(int i=1;i<=n;++i) { 
		if(!vis[i]) 
			dfs(i, 0);
	} 
	printf(OK ? "Yes" : "No");
	return 0;
}  

E题:
这E题不比F题劲多了。
正难则反,一发最短路计数统计出最短路数量,先无脑选,然后干掉不合法的。
“不能在点或边相遇”,想到两种情况都要判。
在点相遇很简单, d i s t [ S ] [ p o s ] = d i s t [ T ] [ p o s ] dist[S][pos]=dist[T][pos] dist[S][pos]=dist[T][pos],且有 d i s t [ S ] [ p o s ] + d i s t [ T ] [ p o s ] = d i s t [ S ] [ T ] dist[S][pos]+dist[T][pos]=dist[S][T] dist[S][pos]+dist[T][pos]=dist[S][T]
经过这个点的方案数咋统计呢?两遍dij,用 S S S p o s pos pos 的方案数量乘上 p o s pos pos T T T的方案数量。
然后考虑在边上撞车,显然撞车的时间点是 d i s t [ S ] [ T ] 2 \frac{dist[S][T]}{2} 2dist[S][T]
对于一个边 ( l , r , l e n ) (l, r, len) (l,r,len),如果有 d i s t [ S ] [ l ] < d i s t [ S ] [ T ] 2 dist[S][l]<\frac{dist[S][T]}{2} dist[S][l]<2dist[S][T],且 d i s t [ r ] [ T ] < d i s t [ S ] [ T ] 2 dist[r][T]<\frac{dist[S][T]}{2} dist[r][T]<2dist[S][T],且 d i s t [ S ] [ l ] + l e n + d i s t [ r ] [ T ] = d i s t [ S ] [ T ] dist[S][l]+len+dist[r][T]=dist[S][T] dist[S][l]+len+dist[r][T]=dist[S][T],那么说明存在在这条边撞上的路径。
乘一乘,减一减,完事。
哦对,WA了一万发是因为,判在边上撞车那里,我成功把代表 d i s t [ S ] [ T ] dist[S][T] dist[S][T]的变量 L e n Len Len打成了边长 l e n len len

#include<bits/stdc++.h>
using namespace std;
#define int long long
struct edge { 
	int to, len;
};
vector<edge>ed[100010];
long long disS[100010], disT[100010], cntS[100010], cntT[100010], vis[100010];
int n, m, S, T;
const int mod = 1000000007;
long long ans = 0;
inline void ade(int l, int r, int val) { 
	ed[l].push_back({r, val});
	ed[r].push_back({l, val});
} 

inline void dij(int SS, int TT, long long *dist, long long *cnt) { 
	for(int i=1;i<=n;++i) { 
		dist[i] = 0x3f3f3f3f3f3f3f3f;
		cnt[i] = 0;
		vis[i] = 0;
	} 
	dist[SS] = 0, cnt[SS] = 1;
	priority_queue<pair<long long, int> >sth;
	sth.push({0, SS});
	while(!sth.empty()) { 
		int nw = sth.top().second; sth.pop();
		if(vis[nw]) continue;
		vis[nw] = 1, cnt[nw] %= mod;
		for(int i=0;i<ed[nw].size();++i) { 
			int tar = ed[nw][i].to, nlen = ed[nw][i].len;
			if(dist[tar] > dist[nw] + nlen) { 
				dist[tar] = dist[nw] + nlen, cnt[tar] = cnt[nw];
				sth.push(make_pair(-dist[tar], tar));
			} 
			else if(dist[tar] == dist[nw] + nlen) { 
				cnt[tar] += cnt[nw], cnt[tar] %= mod;
			} 
		} 
	} 
	return;
} 

signed main() { 
	scanf("%lld%lld", &n, &m);
	scanf("%lld%lld", &S, &T);
	int l, r, val;
	for(int i=1;i<=m;++i) { 
		scanf("%lld%lld%lld", &l, &r, &val);
		ade(l, r, val);
	} 
	dij(S, T, disS, cntS);
	dij(T, S, disT, cntT);
	long long Len = disS[T];
	if(cntS[T] != cntT[S])
		cout<<"FUCK";
	ans += 1ll * cntS[T] * cntS[T];
	ans %= mod, ans += mod, ans %= mod;
	for(int i=1;i<=n;++i) { 
		if(disS[i] + disT[i] == Len && disS[i] + disS[i] == Len && disS[i] == disT[i]) { 
			long long tot = cntS[i] * cntT[i];
			tot %= mod;
			ans -= 1ll * tot * tot;
			ans %= mod, ans += mod, ans %= mod;
		} 
	} 
	for(int i=1;i<=n;++i) { 
		for(int j=0;j<ed[i].size();++j) { 
			int l = i, r = ed[i][j].to;
			int len = ed[i][j].len;
			if(disS[l] + len + disT[r] == Len && 2 * disS[l] < Len && 2 * disT[r] < Len) { 
				long long tot = cntS[l] * cntT[r];
				tot %= mod;
				ans -= 1ll * tot * tot;
				ans %= mod, ans += mod, ans %= mod;
			} 
		} 
	} 
	cout<<ans;
	return 0;
} 

F题:
开心快乐计数题。
假设 [ l , r ] [l, r] [l,r]里头有 n n n个数字。若这 i i i个数字的 f f f值都一样,那么就显然很好搞。
如果不一样的话,我们发觉如果有两种,那么还稍微好搞一点,如果种类多于两种,就不那么好搞了。
然后发觉,如果多于两种,肯定是完整包含了一大坨东西,这一坨东西显然最多是7位数。
然后就小数字搞个双指针扫一扫,大数字判一判。
判的时候,枚举 [ r , l ] [r,l] [r,l]里头有多少个数字,令他为 i i i。如果 n % i = = 0 n\%i==0 n%i==0,那么说明存在 “ [ l , r ] [l,r] [l,r]里头 f f f值一样” 的情况,快速幂算一算方案数。如果 n % i ! = 0 n\%i!=0 n%i!=0,那么说明有个分界点,显然这个只有一种情况。

#include<bits/stdc++.h>
using namespace std;
int f[30000000];
const int maxn = 29999999;
int n;
long long ans = 0;
const int mod = 1000000007;

long long fpow(long long di, long long top) { 
	long long ret = 1;
	while(top) { 
		if(top % 2) 
			ret = ret * di % mod;
		di = di * di % mod;
		top /= 2;
	} 
	return ret;
} 

int main() { 
	f[1] = 1, f[10] = 2, f[100] = 3, f[1000] = 4, f[10000] = 5, f[100000] = 6, f[1000000] = 7, f[10000000] = 8;
	for(int i=1;i<=maxn;++i) { 
		f[i] = f[i] == 0 ? f[i-1] : f[i];
	} 
	int nr = 1, nsum = 1;
	cin>>n;
	while(nsum < n) 
		nsum += f[++nr];
	ans += (nsum == n);
	for(int i = 1; i < 10000000 - 1;++i) { 
		nsum -= f[i];
		while(nsum < n) { 
			nsum += f[++nr];
		} 
		ans += (nsum == n);
	} 
	for(int i = 1; i <= n / 8; ++i) { 
		if(n % i == 0) { 
			long long tot = fpow(10, n/i);
			long long res = fpow(10, n/i - 1);
			long long fin = tot - res - i;
			ans += fin + 1;
			ans %= mod, ans += mod, ans %= mod;
		} 
		else {
			ans++;
			ans %= mod, ans += mod, ans %= mod;
		} 
	} 
	cout<<ans;
	return 0;
} 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值