1月6日考试总结

考炸了,赛时只做出了一道题。

A 过关斩将

做法

这道题就是一个很显然的二维最短路,设 \(dis[i][j]\) 表示到达点 \(i\) 且当前的状态为 \(j\) 的最少代价。其中 \(j=0\) 时表示状态为 \(L\)\(j=1\) 时表示状态为 \(R\)

很显然可以用 \(dijkstra\) 来求解,转移方程为 \(dis[v][v.type] = dis[u][u.type] + w(u.type==v.type)\)

也可以为 \(dis[v][u.type] = dis[u][u.type] + w(v.type==M)\)

还有种情况 \(dis[v][v.type] = dis[u][u.type] + w + x(v.type\ne u.type ~~ and ~~ v.type \ne 2)\)

答案为 \(min(dis[t][0],dis[t][1])\)

\(Code\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+50;
int T,cnt;
int n,m,s,t,x;
int head[N];
struct edge{
	int to,nxt,w;
}e[N*4];
void add(int u,int v,int f)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
	e[cnt].w=f;
	return;
}
string ss;
bool vis[N][4];
int dis[N][4];
struct node{
	int val,pos,type;
	bool operator >(const node &x)const
	{
		return val>x.val;
	}
};
void dij(int S)
{
	for(int i=1;i<=n;i++){
		for(int j=0;j<=2;j++)
		{
			dis[i][j]=1e18;
			vis[i][j]=0;
		}
	}
	if(ss[S]=='L')dis[S][0]=0;
	else if(ss[S]=='R')dis[S][1]=0;
	else if(ss[S]=='M')dis[S][0]=0,dis[S][1]=0;
	priority_queue<node,vector<node>,greater<node> >q;
	q.push(node{dis[S][0],S,0});q.push(node{dis[S][1],S,1});
	while(!q.empty())
	{
		node t=q.top();q.pop();
		if(vis[t.pos][t.type])continue;
		vis[t.pos][t.type]=1;
		for(int i=head[t.pos];i;i=e[i].nxt)
		{
			int v=e[i].to;
			int op=-1;
			if(ss[v]=='L')op=0;
			else if(ss[v]=='R')op=1;
			else if(ss[v]=='M')op=2;
			if(op==t.type&&dis[v][op]>dis[t.pos][t.type]+e[i].w)
			{
				dis[v][op]=dis[t.pos][t.type]+e[i].w;
				q.push(node{dis[v][op],v,op});
			}
			else if(op==2&&dis[v][t.type]>dis[t.pos][t.type]+e[i].w)
			{
				dis[v][t.type]=dis[t.pos][t.type]+e[i].w;
				q.push(node{dis[v][t.type],v,t.type});
			}
			else if(dis[v][op]>dis[t.pos][t.type]+x+e[i].w)
			{
				dis[v][op]=dis[t.pos][t.type]+e[i].w+x;
				q.push(node{dis[v][op],v,op});
			}
		}
	}
	return;
}
signed main()
{
	scanf("%lld",&T);
	while(T--)
	{
		cnt=0;
		scanf("%lld %lld %lld %lld %lld",&n,&m,&s,&t,&x);
		for(int i=1;i<=n+50;i++)head[i]=0;
		cin>>ss;
		ss=" "+ss;
		for(int i=1,u,v,w;i<=m;i++)
		{
			scanf("%lld %lld %lld",&u,&v,&w);
			add(u,v,w);add(v,u,w);
		}
		dij(s);
		printf("%lld\n",min(dis[t][0],dis[t][1]));
	}
	return 0;
}

B 翻转游戏

似乎又是一道奇偶性好题? 可我没想到。(悲)

做法

因为从小到大翻转不好翻,所以考虑从大到小翻转,这样做不会对答案产生影响,因为翻转就相当于区间异或,异或运算满足交换律。

那么就这样,如果当前翻转的区间的长度小于等于剩余 \(1\) 的区间长度那就直接翻转。如果当前翻转的区间长度大于剩余 \(1\) 的区间长度那就以剩余 \(1\) 的区间的右端点左右翻转区间的右端点进行翻转。

显然这样子做不会有问题,每次(除最后一次)区间 \(1\) 的个数都会增加或减少偶数个( \(2^{i} ~~ (i\ge 2)\) ),但最后一次区间 \(1\) 的个数会减少奇数个,所以无解的情况就应该为原来区间 \(1\) 的长度为偶数,那么无论怎样又不可能把这个区间 \(1\) 的个数减少为 \(1\)

\(Code\)

#include<bits/stdc++.h>
using namespace std;
int T;
int n,k,tot;
int ans[35];
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d %d",&n,&k);
		if(k==0)
		{
			printf("YES\n0\n");
			continue;
		}
		if(!(k&1))
		{
			printf("NO\n");
			continue;
		}
		printf("YES\n");
		int x=log2(k),R=k,r=1;tot=0;
		for(int i=x;i>=0;i--)
		{
			if((1<<i)<=R-r+1)
			{
				ans[++tot]=r;
				r+=(1<<i);
			}
			else
			{
				int tmp=R;
				ans[++tot]=R-(1<<i)+1;
				R=r-1;
				r=tmp-(1<<i)+1;
			}
		}
		printf("%d\n",tot);
		for(int i=tot;i>=1;i--)printf("%d ",ans[i]);
		printf("\n");
	}
	return 0;
}

C 大模法师

很相似的一道题

以前做过,可是赛上只有一个人做出来且不是用的原来的方法。所以这道题有很多战犯

貌似只有我只会随机化?

做法

假如两个数 \(a=k_1\times m+b_1,b=k_2\times m+b_2\) 在最终的答案集合中,根据同余的性质可得 \(m|a-b\) ,所以每次随机两个数再做差得到 \(x\) ,枚举 \(x\) 的正整数因子即可。

接下来分析每次 \(check\) 的正确率,两个数都在答案集合的概率为 \(\frac{1}{2}\times \frac{1}{2}=\frac{1}{4}\) ,所以每次失败的概率为 \(1-\frac{1}{4}=\frac{3}{4}\),多随机个几十次就行了或者直接卡时。

\(Code\)

代码实现起来还是有很多细节的,比如每次枚举因子可以随机化,因为已经在之前枚举过了;如果最开始相同的个数就已经超过一半了,那就直接输出 \(100000\) 。还有一个优化:如果一个数不行,那么它的倍数就一定不行,举个栗子如果每个数 \(\mod 3\) 以后还是不行,那么 \(\mod 6,9,...\) 都不行,因为比如之前 \(\mod 3=1\) 的数在\(\mod 6\) 后的结果可能为 \(1(1\mod 6),4(4 \mod 6)\)\(\mod 9\) 后的结果可能为 \(1(1\mod 9),4(4\mod 9),7(7\mod 9)\) ,所以就更不可能模数相同的超过一半了。(话说好像只有这个优化就能通过本题了?

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+50;
int a[N],n,ans,cntt[N],mp[N],minn=9999999,maxn=-9999999;
bool vis[N];
bool check(int x)
{
	memset(mp,0,sizeof(mp));
	for(int i=1;i<=n;i++)
	{
		int u=a[i]%x;
		mp[u]++;
		if(mp[u]>=ceil(1.0*n/2))
			return true;
	}
	return false;
}
void divide(int x)
{
	for(int i=1;i*i<=x;i++)
	{
		if(x%i==0&&(i>ans||x/i>ans))
		{
			if(vis[i]==0)
			{
				vis[i]=1;
				if(check(i))
					ans=max(ans,i);
				else
					for(int j=i*2;j<=maxn;j+=i)vis[j]=1;
				
			}
			if(vis[x/i]==0)
			{
				vis[x/i]=1;
				if(check(x/i))
					ans=max(ans,x/i);
				else
					for(int j=(x/i)*2;j<=maxn;j+=(x/i))vis[j]=1;
			}
		}
		else continue;
	}
	return;
}
mt19937 rd(1936);
int main()
{
	double s=clock();
	srand(time(0));
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),cntt[a[i]]++,minn=min(minn,a[i]),maxn=max(maxn,a[i]);
	for(int i=minn;i<=maxn;i++)
	{
		if(cntt[i]>=ceil(1.0*n/2))
		{
			printf("100000\n");
			return 0; 
		}
	}
	if(n<=1000&&maxn<=1000)
	{
		ans=1;
		for(int i=2;i<=maxn;i++)
		{
			memset(mp,0,sizeof(mp));
			for(int j=1;j<=n;j++)
			{
				mp[a[j]%i]++;
				if(mp[a[j]%i]>=ceil(1.0*n/2))
				{
					ans=max(ans,i);
					break;
				}
			}
		}
		printf("%d\n",ans);
		return 0;
	}
	ans=1;
	vis[1]=1;
	while(clock()-s<=1980)
	{
		int x=0,y=0;
		while(a[x]==a[y])
			x=rd()*rd()%n+1,y=rd()*rd()%n+1;
		int z=abs(a[x]-a[y]);
		if(z==1||z==0)continue;
		divide(z);
	}
	printf("%d\n",ans);
	return 0;
}

D 彩色的树

赛后觉得唯一一个偏正常的题目。

看到多重 \(\sum\) 求和的式子就应该套路的想到单独拆开计算每个点的贡献。

\(dfs\) 的过程中统计即可,计算剩下的连通块,注意根节点所在的连通块即可,不得不说 \(dfs\) 的思想真的很妙呀。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+50;
int n,tot;
int color[N],cnt[N],siz[N],Tot[N];
struct node{
	int nxt,to;
}e[N*2];
int head[N];
void add(int u,int v)
{
	e[++tot].to=v;
	e[tot].nxt=head[u];
	head[u]=tot;
	return;	
}
void dfs(int u, int fa)
{
	int mark = cnt[color[u]];
	siz[u] = 1;
	for (int j = head[u]; j != 0; j = e[j].nxt)
	{
		int v = e[j].to;
		if (v == fa) continue;
		dfs(v, u);
		siz[u] += siz[v];
		int t = siz[v] - (cnt[color[u]] - mark);
		Tot[color[u]] += (long long)t * (t - 1) / 2;
		cnt[color[u]] = mark;
	}
	cnt[color[u]] = mark + siz[u];
	return;
}
signed main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)scanf("%lld",&color[i]);
	for(int i=1,u,v;i<=n-1;i++)
	{
		scanf("%lld %lld",&u,&v);
		add(u,v);add(v,u);
	}
	dfs(1,0);
	for (int i = 1; i <= n; i++)
		Tot[i] += (long long)(siz[1] - cnt[i]) * (siz[1] - cnt[i] - 1) / 2;
	long long ans = 0; 
	for (int i = 1; i <= n; i++)
		ans += (long long)siz[1] * (siz[1] - 1) / 2 - Tot[i];
	printf("%lld\n",ans);
	return 0;
}
posted @ 2024-01-07 11:14  CQWYB  阅读(6)  评论(0编辑  收藏  举报