加载中…

返回上一页

2022NOIP A层联测31

下发文件(密码为原 accoders 比赛密码)

不稳定的道路

就是把普通的 dijkstra 稍微修改一下.

一看到这个 \frac{d[i]}{t+1},又因为总共的 dis 值是 t,所以自然就能拆成 \frac{d[i]}{t+1}+t+1-1.

这是个基本不等式,最终答案会在 \sqrt{d[i]}-1 \sim \sqrt{d[i]} 处取得. 如果当前的 t 已经比它大,就取当前的 t 这个位置.

我才发现我打了这么多年来的 dijkstra 居然是假的!

原因在这里:if(fl[t]) continue; fl[t]=1; 我没有定义是否访问过!吃一堑长一智

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 200001
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar(); while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
struct node { ll to,c,d; };
ll n,m,dis[maxn];
vector<node> g[maxn];
priority_queue<pll,vector<pll>,greater<pll> > q;
bool fl[maxn];
inline void dij(rll x)
{
	memset(dis,0x3f,sizeof(dis)); q.push((pll) { dis[x]=0,x });
	while(!q.empty())
	{
		rll t=q.top().second;q.pop();
		if(fl[t]) continue; fl[t]=1;
		for(rll i=0;i<g[t].size();i++)
		{
			rll to=g[t][i].to,d,sq=__builtin_sqrtl(g[t][i].d);
			if(sq<=dis[t]) d=dis[t]+g[t][i].c+g[t][i].d/(dis[t]+1); else d=g[t][i].c+min(g[t][i].d/sq+sq-1,g[t][i].d/(sq+1)+sq);
			if(dis[to]>d) q.push((pll) { dis[to]=d,to });
		}
	}
}
int main()
{
	freopen("road.in","r",stdin); freopen("road.out","w",stdout);
	n=read();m=read();// if(n==50002&&m==99999) { puts("1999950002");return 0; }
	for(rll i=1,u,v,c,d;i<=m;i++) u=read(),v=read(),c=read(),d=read(),g[u].push_back((node) { v,c,d }),g[v].push_back((node) { u,c,d });
	dij(1); if(dis[n]==0x3f3f3f3f3f3f3f3f) puts("-1"); else write(dis[n]);
	return 0;
}

翻转有向图

一看题目里都说了强连通分量,肯定就不会让你去打 tarjan.

一共就是四种情况:

设当前边是 u → v,有:

  1. 如果有一条其他从 uv 的路径,且有一条从 vu 的路径,那么影响;

  2. 如果没有一条其他从 uv 的路径,且有一条从 vu 的路径,那么影响(会减少);

  3. 如果有一条其他从 uv 的路径,且没有一条从 vu 的路径,那么影响(会增加);

  4. 如果没有一条其他从 uv 的路径,且没有一条从 vu 的路径,那么影响.

那么考虑怎么去求. 直接爆搜肯定是不行的,比暴力跑 tarjan 都慢.

发现这四种情况可以转化为两类方案:是否存在反向路径、当前的边是否为必须经过的边(就是是否有其他边).

这东西还是怎么去得到,可以联想二分图的知识:染色法. 染够一个就跳. 因此维护两个数组即可.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 1001
#define maxm 200001
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar(); while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
ll n,m,u[maxm],v[maxm],a[maxn][maxn],b[maxn][maxn],ans;
vector<ll> g[maxn];
inline void dfs(rll x,rll st,rll* c)
{
	if(c[x]) return; c[x]=st;
	for(rll i=0;i<g[x].size();i++) { rll to=g[x][i]; dfs(to,st,c); }
}
int main()
{
	freopen("turn.in","r",stdin); freopen("turn.out","w",stdout);
	// freopen("in.txt","r",stdin); freopen("out.txt","w",stdout);
	n=read();m=read(); for(rll i=1;i<=m;i++) u[i]=read(),v[i]=read(),g[u[i]].emplace_back(v[i]);
	for(rll i=1;i<=n;i++)
	{
		a[i][i]=b[i][i]=1; for(rll j=0;j<g[i].size();j++) dfs(g[i][j],g[i][j],a[i]);
		reverse(g[i].begin(),g[i].end()); for(rll j=0;j<g[i].size();j++) dfs(g[i][j],g[i][j],b[i]);
	}
	for(rll i=1;i<=m;i++) puts(((bool)(a[u[i]][v[i]]^b[u[i]][v[i]])^(bool)a[v[i]][u[i]])?"diff":"same");
	return 0;
}

在表格里造序列

不会杜教筛,也不想学,打了个暴力溜了……

zzzyyds

由于是等概率的,因此用组合数直接转移没有问题,只计算总的数量,就不需要去单独考虑每个情况了.

dp[i][j] 为当前分成了 i 个段(连续的),当前剩下 j 个空位,期望还需要几次才能把数列填满.

转移分以下 5 种:

(无耻放图)

解释一下为什么放到长度为 2 的方案数要乘 2:显然 因为两个位置可以放在左边也可以放右边,然后另一个夹着的自动就补齐了;而 3 个的只能放中间,否则无法自动补齐.

统计答案的时候就按照期望的线性性,直接累加从 k + 1n - 1dp 值即可.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define pll pair<ll,ll>
#define maxn 2001
#define mod 998244353
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar(); while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
ll n,k,t,ans,jc[maxn],ny[maxn],dp[maxn][maxn];
inline ll ksm(rll a,rll b) { rll ans=1;a%=mod; for(rll i=b;i;i>>=1) { if(i&1) (ans*=a)%=mod; (a*=a)%=mod; } return ans; }
inline ll C(rll n,rll m) { if(!~(n&m)) return 1; if(n<m) return 0; return jc[n]*ny[m]%mod*ny[n-m]%mod; } 
int main()
{
	freopen("holiday.in","r",stdin); freopen("holiday.out","w",stdout);
	jc[0]=1; for(rll i=1;i<maxn;i++) jc[i]=jc[i-1]*i%mod; ny[maxn-1]=ksm(jc[maxn-1],mod-2); for(rll i=maxn-2;~i;i--) ny[i]=ny[i+1]*(i+1)%mod;
	n=read();k=read();t=read(); dp[n-1][1]=1;
	for(rll i=n-1,sum,t,ny;i;i--) for(rll j=1;j<=n;j++) if(i>j&&j&&C(i-j-1,j-1)&&dp[i][j])
	{
		sum=i*C(i-j-1,j-1)%mod;ny=dp[i][j]*ksm(sum,mod-2)%mod;// cout<<sum<<' '<<ny<<endl;
		t=(j<<1)*C(i-2-(j-1)-1,j-1-1)%mod; if(i>=2) (dp[i-2][j-1]+=t*ny%mod)%=mod;sum-=t;// cout<<t<<' ';
		t=j*C(i-3-(j-1)-1,j-1-1)%mod; if(i>=3) (dp[i-3][j-1]+=t*ny%mod)%=mod;sum-=t;
		// cout<<j<<' '<<i-3-(j-1)-1<<' '<<j-1-1<<' '<<C(i-3-(j-1)-1,j-1-1)<<' '<<t<<endl;
		(dp[i-1][j]+=(t=(j<<1)*C(i-1-j-1,j-1)%mod)*ny%mod)%=mod;sum-=t;// cout<<t<<' ';
		t=(j<<1)*C(i-2-j-1,j-1)%mod; if(i>=2) (dp[i-2][j]+=t*ny%mod)%=mod;sum-=t;// cout<<t<<endl;
		sum=(sum%mod+mod)%mod;
		(dp[i-1][j+1]+=sum*ny%mod)%=mod;// ,cout<<sum<<'*'<<ny<<' '<<dp[2][2]<<endl;
		// cout<<i<<' '<<j<<' '<<dp[i][j]<<endl;
	}
	for(rll i=n-1;i>=k+1;i--) for(rll j=1;j<=n;j++) (ans+=dp[i][j]*n%mod*ksm(i,mod-2)%mod*ksm(n-i,t)%mod)%=mod;// ,cout<<i<<' '<<j<<' '<<dp[i][j]<<endl;
	write(ans);
	return 0;
}

为 梦 想 而 战 !

posted @ 2022-11-19 17:58  1Liu  阅读(23)  评论(0编辑  收藏  举报