11.4 模拟赛

这分数羞于出口(fuck)

T1 大空魔术

题目:

宇佐见莲子是一个向往太空旅行的大学生。但迫于月面旅行团高额的费用,她暂时放弃了幻想,开

始专心于物理学的研究。

在一次偶然的经历中,莲子发现了两种新的粒子,她将这两种粒子命名为「粒子 A」和「粒子 B」 。

莲子发现,两种粒子在不受外界刺激的情况下可以保持稳态,并且会相互吸引。多个粒子在相互的

吸引力作用下会形成一个有序的序列。

莲子还发现,当特定种类的两个粒子以特定方式排列时,给予它们一种特定的刺激,就会发生「互

毁」现象:两个粒子会发生完全的物质-能量转换,转化为大量能量。

经过长时间的研究,莲子发现了「互毁」现象发生的条件:当前仅当相邻的两个粒子呈现:”AB”

或”BB” 的形式时,给予特定刺激后它们才会发生「互毁」 。

现在,莲子在实验室中将众多粒子排成了多个序列,她想通过给予刺激的方式,用这些粒子得到尽

可能多的能量,即留下尽可能少的粒子。

由于粒子会相互吸引,每当相邻的两个粒子发生「互毁」后,它们左右两侧的粒子还会拼在一起,

仍然保持一个序列的形态。

但粒子数实在是太多了,于是她找到你,请你帮助她求出最后剩余粒子数的最小值。

输入:

第 1 行一个整数 t,代表粒子序列的个数。

第 2∼ t 行,第 i + 1 行包含一个只由字符 'A' 和 'B' 构成的字符串 \(s_i\) ,描述了一个粒子序列。

输出:

一个整数,代表最后剩余粒子数

样例:

3

AAA

BABA

AABBBABBBB

3

2

0

解题思路:

对于这个有序序列,我们能删就删,尽管有 ABBA这种情况,我们发现会有 两种删法,但是它产生的贡献却都是一样的,都是 2,所以我们可以根据这个贪心,枚举子串,### 利用一下栈,然后碰上A就入栈,碰上B就出栈,因为无论什么情况,造成出栈的一定是有B的子串,所以枚举到最后只需要看一下还剩下多少就OK了,

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <cstdlib>
#define inf 0x3f
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){ if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){ x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
std::stack<char> s; 
int main()
{
	int t=read();
	while(t--)
	{
		std::string ch;
		std::cin>>ch;
		for(int i=0;i<=ch.length();i++)
		{
			if(ch[i]=='A')
			{
				s.push(ch[i]);
				continue;
			}
			else if(ch[i]=='B')
			{
				if(s.empty()) 
				{
					s.push(ch[i]);
				}
				else 
				{
					//char top=s.top();
					s.pop();	
				}	
			}
		}
		printf("%d\n",s.size());
	}
	return 0;
}

T2 夜桜街道

题目描述:

深夜,离开实验室后,莲子与好友梅丽相约来到了一条长满樱花树的街道,她们决定从街道的左侧

某个位置出发向右走,欣赏沿途的每一棵樱花。

她们发现,这条街道上共有 n 棵樱花树,从左到右的编号依次为 1,2,···n,每一棵樱花树都有自

己的美丽值,编号为 i 的樱花树的美丽值为\(a_i\)

莲子喜欢惊喜的感觉。若她以某个位置作为起点向右走,她看到一棵樱花树的惊喜度定义为:从起

点到这棵树途经的所有树中,比这棵树的美丽值小的个数。

梅丽决定用平均值描述莲子的平均惊喜度。她定义区间 [l,r] 的平均惊喜度为,从樱花树 l 开始走,

向右走到樱花树 r 时,看到所有樱花树的惊喜度之和,除以区间中樱花树的个数。

现在,莲子和梅丽想知道,以樱花树 1 为起点,分别以樱花树 1 ∼ n 为终点时,莲子的平均惊喜

度之和。

由于她俩都不喜欢小数,你只需要输出这个值对 998244353 取模的结果。

输入:

第 1 行一个整数 n,表示樱花树的棵树。

第 2 行有 n 个整数,第 i 个整数表示编号为 i 的樱花树的美丽值\(a_i\)

输出:

一行一个整数,表示答案。

样例:

6

1 1 4 5 1 4

748683269

数据:

对于测试点 1 ∼ 6 ,保证 n ≤ 1000。

对于测试点 1 ∼ 10,保证 n ≤ 5000。

对于测试点 1 ∼ 20,保证 n ≤\(10^6\)

对于所有测试点,保证 n ≤\(10^6\) ,0 ≤\(a_i\)\(2^{30}-1\)

解题思路:

我们输入每个\(a_i\)的时候我们可以用树状数组去维护一下它前面的比它小的数,怎么统计,我们就可以考虑权值树状数组,但是数据开不了,所以需要离散化一下,然后在,如果

离散化的话,就求个顺序对,然后,再利用逆元,求解就行了。

代码:

未进行离散化的

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <cstdlib>
#define inf 0x3f
#define lowbit(x) x&(-x)
#define int long long
const int maxn=1e6;
const int mod=998244353;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){ if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){ x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n; 
int num[maxn],s[maxn],tree[maxn],inv[maxn];
int MAX;
void add(int x,int k)
{
	for(int i=x;i<=MAX;i+=lowbit(i))
	{
		tree[i]++;
	}
}
int find(int x)
{
	int sum=0;
	for(int i=x;i>=1;i-=lowbit(i))
	{
		sum+=tree[i];
	}
	return sum;
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		num[i]=read();
		MAX=std::max(MAX,num[i]);
	}
	for (int i = 1; i <= n; i++)
	{
		s[i]=find(num[i]-1);//小于等于的为 find(num[i]), 
		//std::cout<<s[i]<<"TEXT"<<std::endl;
		add(num[i],1);
	}
	int ans=0;
	inv[1]=1;
	/*for(int i=1;i<=n;i++)
	{
		// -k * r_-1
		inv[i]=(-p/i)*inv[p%i];
	} */
	for(int i=2;i<=n;i++)
	{
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	}
	for(int i=1;i<=n;i++)
	{
		s[i]+=s[i-1];
		s[i]%=mod;
		ans=(ans+inv[i]%mod*s[i])%mod;
		ans%=mod;
//		std::cout<<inv[i]<<std::endl;
//		std::cout<<ans<<std::endl;
	}
	printf("%lld",ans);
	return 0;
}

进行了离散化的

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <cstdlib>
#define inf 0x3f
#define lowbit(x) x&(-x)
#define int long long
const int maxn=1e6;
const int mod=998244353;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){ if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){ x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n; 
int num[maxn],s[maxn],tree[maxn],inv[maxn];
int MAX;
void add(int x,int k)
{
	for(int i=x;i<=MAX;i+=lowbit(i))
	{
		tree[i]++;
	}
}
int find(int x)
{
	int sum=0;
	for(int i=x;i>=1;i-=lowbit(i))
	{
		sum+=tree[i];
	}
	return sum;
}
int b[maxn];//离散化用的数组 
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		num[i]=read();
		b[i]=num[i];
		MAX=std::max(MAX,num[i]);
	}
	std::sort(b+1,b+n+1);//从小到大排序
	int len = std::unique(b+1,b+n+1)-b-1; 
	for(int i=1;i<=n;i++)
	{
		num[i]=std::lower_bound(b+1,b+len+1,num[i])-b;
		MAX=std::lower_bound(b+1,b+len+1,MAX)-b;
	}
	for (int i = 1; i <= n; i++)
	{
		s[i]=find(num[i]-1);//小于等于的为 find(num[i]), 
		//std::cout<<s[i]<<"TEXT"<<std::endl;
		add(num[i],1);
	}
	int ans=0;
	inv[1]=1;
	/*for(int i=1;i<=n;i++)
	{
		// -k * r_-1
		inv[i]=(-p/i)*inv[p%i];
	} */
	for(int i=2;i<=n;i++)
	{
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	}
	for(int i=1;i<=n;i++)
	{
		s[i]+=s[i-1];
		s[i]%=mod;
		ans=(ans+inv[i]%mod*s[i])%mod;
		ans%=mod;
//		std::cout<<inv[i]<<std::endl;
//		std::cout<<ans<<std::endl;
	}
	printf("%lld",ans);
	return 0;
}

T3科学世纪

问题描述:

t 组数据,每次给定整数 p,q,求一个最大的整数 x,满足 p 可以被 x

整除,并且 q 不能整除 x。

输入:

第 1 行一个整数 t,代表数据组数。

第 2 ∼ t + 1 行,每行两个整数 p,q,代表给定的两个参数。

输出:

共 t 行,每一行代表对应的 x 的值。

样例:

输入:

3

10 4

12 6

179 822

输出:

10

4

179


思路分析:

显然,如果 \(p<q\),那么\(p|x\)的时候,\(p\not|x\),很显然,\(p\)就符合输出的条件,那么我们这个时候输出p 就好了,同时,\(p\) % \(q\) !=0的时候,也就输出\(p\)就好了,

那现在就是考虑其他情况的时候,我们考虑一下,\(p\)\(q\)的质因子,但是\(p\)中有比\(q\)质因子大的,但它对答案并不产生贡献,我们做的是枚举删除某个质因子,然后寻求最大值(为什么要删去一个,删去多个就会使得\(x\)下降,导致不是最优解)

所以解法就是 :枚举 \(q\)的每一个质因子,然后令\(p\)去除以它,直到\(p\)中的质因子的数要小于\(q\)中质因子数,然后比较出最大值来,然后就是答案;

总复杂度 O(\(t\sqrt{q}\)


先对 \(p,q\) 质因数分解,设 \(\{a_i\}\) 为质数集,\(\{b_i\}\) 为对应质数的次数:

$$p=\prod a_{i}^{b1_i}$$

$$q=\prod a_{i}^{b2_i}$$

\((x|p) \land (q\nmid {x})\),则 \(x\) 质因数分解后有:

$$x=\prod a_{i}^{b3_i}$$

$$p=k\times x =k\times \prod a_{i}^{b3_i}(k\in \mathbf{N}^*)$$

$$∃a_j|q,\ b3_j < b2_j$$

第二个条件表示 \(x|p\),第三个条件表示存在一个整除 \(q\) 的质数 \(a_j\),它在 \(x\) 中的次数比在 \(q\) 中的次数要小,从而使 \(q\nmid x\)

显然,最优的 \(x\) 一定是 \(p\) 抠去一些质因子 \(a_j\),使得该质因子在 \(p\) 中的次数小于 \(q\) 中的次数后剩下的值。

显然抠去的质因子最多有一个。

所以可以枚举 \(q\) 的所有质因子并进行计算。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define int long long
#define INF (1e13 + 7)
#define MANX MAXN
#define MAXN 2000000

using namespace std;

inline int read()
{
	int x = 0, f = 1; char c = getchar();
	while (c > '9' || c < '0') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') {x = x * 10 + (c ^ 48); c = getchar();}
	return f * x;
}

int a[MAXN], b[MAXN], numa, numb;

signed main()
{
	int t = read();

	while (t--)
	{
		int p = read(), q = read();
		
		if (q > p || p % q != 0) {cout << p; puts(""); continue;}//---
		
		int x = p, y = q, ans = 1;//---
		
		for (int i = 2; i <= sqrt(y); i++)
		{
			if (y % i == 0)
			{
				numa = 0, numb = 0;
				while (x % i == 0) x /= i, numa++;
				while (y % i == 0) y /= i, numb++;
				int num = numa - numb + 1, k = p;//p的质数的数量减去q质数的数量 
				for (int j = num; j; j--) k /= i;
				ans = max(ans, k);
			}
		}
		
		if (y != 1)
		{
			numa = 0, numb = 0;
			while (x % y == 0) x /= y, numa++;
			numb++;
			int num = numa - numb + 1, k = p;
			for (int i = num; i; i--) k /= y;
			ans = max(ans, k);
		}

		cout << ans;
		puts("");
	}
	return 0;
}

\(T4\), \(萃香摘西瓜\)

题目描述

萃香所处的环境被简化为一个长为h,宽为w的网格平面。X坐标范围为[1,w],y坐标范围为[1,h]。

她初始(第1个时刻)站在坐标为sx,sy的方格。

西瓜可能在任意一个方格出现,在每个时间单位,它们可能向任何一个方向移动,也可能静止不动。西瓜的位置和移动的轨迹是已知的。西瓜的总数为n个,但只有m个西瓜可以被萃###香抱走,因为其他都太大了,可能会砸伤她。

整个过程会持续T个时刻。萃香希望可以抱走全部的m个西瓜,并且在任何时候避免与任何一个过大的西瓜处在同一位置。抱走的方式为在某个时刻,与该西瓜处于同一位置。另外,###由于萃香不愿耗费过多体力到处乱跑,她每个时刻可以选择静止不动,也可以选择移动到相邻的四个格子之一,只要不越出环境边界。如果选择移动到相邻格子,则算作移动了一次。###(第1个时刻萃香刚站定,无法移动)

在每个时刻,如果萃香选择移动,可以认为萃香与西瓜同时从原来的位置移到了新的位置,没有先后顺序。

萃香想要知道,不被任何一个大西瓜砸中,并得到所有的m个小西瓜的情况下,最少需要移动多少次。

输入格式

第一行五个整数h,w,T,sx,sy,含义见题目描述。

第二行两个整数n,m,含义见题目描述。

接下来n段数据,每一段描述了一个西瓜的出现位置,时间,移动方式,是否可以被抱走等内容,具体如下:

首先一行,两个整数t1,t2,表示西瓜在t1时刻出现, t2时刻消失。若t2=T+1,表示西瓜在最后一个时刻也不消失。保证西瓜至少存在一个时刻。

接下来一行一个整数a,只能为0或1,0表示这个西瓜需要避开,1表示这个西瓜需要抱走。数据保证需要抱走的西瓜恰好有m个。

接下来t2-t1行,每一行两个整数x,y,顺序描述了从t1时刻到t2-1时刻,该西瓜的坐标。西瓜的移动不一定是连续的,并且是一瞬间完成的,所以无需考虑萃香是否站在了移动路径###上。

输出格式

如果萃香在整个T时刻内无法避免被大西瓜砸中或者无法收集到所有m个小西瓜,输出-1,否则输出一个整数,表示萃香需要移动的最少次数。

输入输出样例

输入

5 5 10 3 3

1 1

1 11

1

3 4

5 2

3 5

1 1

5 4

3 4

2 1

1 1

1 1

5 5

输出

1

说明/提示

样例说明:第2~4个时刻萃香站着不动,在第6个时刻,西瓜出现在萃香旁边,萃香移动到(3,4)位置即可抱走这个西瓜。

数据范围和提示:

子任务可能出现两种特殊性质A和B

A: 所有西瓜t1=1,t2=T+1

所有西瓜全程都静止在原地,不会发生移动。

B:m=0

共有10个子任务。

对于子任务1,具有特殊性质A和B

对于子任务2~3,仅具有特殊性质A

对于子任务4~5,仅具有特殊性质B

对于子任务6~10,不具有任何一个特殊性质。

对于全部子任务

1<=所有横坐标范围<=w

1<=所有纵坐标范围<=h

1<=h,w<=5

1<=T<=100

1<=t1<=T

2<=t2<=T+1

t1<t2

1<=n<=20

0<=m<=10

m<=n

一个位置不会同时出现两个或以上西瓜。

考试的时候我想到了状态压缩\(DP\),然后我觉得,西瓜的状态我就多设一维来表吧,好理解,还容易推状态转移方程(然而,我并没有推出来,只好打了个暴力,骗了点分)

首先说一下为什么不能多设一维表示状态,

按学长的话,就是多设一维的后对答案没有贡献,只是判断了下一个点是能够走罢了

解题思路:

我们对西瓜进行状态压缩,然后我们设一个all将m转化成状态压缩,然后我们在读入的时候,进行一下预处理,预处理的就是类似于画一个地图 \(s\),然后\(s_{t,x,y}\)就表示在t时刻,坐标为\((x,y)\)的西瓜的存在状态;

同时我们设一个 \(f_{time,x,y,sum}\)表示t时刻,到坐标\((x,y)\)取得sum个西瓜的最小的步数

同时我们所求的为最短路径,那么我们也就可以直接将\(f\)预处理为最大值,既能跑最短路还能判断一下是否能得到 \(m\)个西瓜,自然要是\(ans==inf\) 那就代表走不了,(既包含不能得到\(m\)个西瓜,也包含无路可走的情况(一样,无路可走也就没办法获得\(m\)个西瓜了))

转移时枚举每个时刻每个位置,从能到达该位置的上个时刻的位置 (x′,y′)

转移过来,还需要枚举上个位置的小西瓜状态;

所以状态转移方程就出来了:

\(f_{t,x,y,\huge s|s_{t,x,y}}=min{f_{t-1,x',y',\huge s}+1}\);

代码:

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#define LL long long
const int maxn = 1024 + 1;
const int inf = 0x3f3f3f3f;
const int dx[5] = {0, 0, 0, 1, -1};
const int dy[5] = {0, 1, -1, 0, 0};
int h, w, t, sx, sy;
int n, m, all, t1, t2;
int f[101][10][10][maxn];
int sum, s[101][10][10];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){ if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){ x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
void swapmin(int &a, int b) 
{
  if (b < a) a= b;
}
void prepare() 
{
  h = read(), w = read(), t = read();
  sx = read(), sy = read();
  n = read(), m = read();
  for (int i = 1; i <= n; ++ i) 
  {
    t1 = read(), t2 = read();
    int type = read();
    if (type) sum ++;
    for (int j = t1; j < t2; ++ j) 
	{
      int x = read(), y = read();
      if(type)
      {
      	s[j][x][y]=(1<<(sum-1));
	  }
	  else 
	  {
	  	s[j][x][y]=-1;
	  }
    }
  }
}
int main() 
{
  prepare();
  if (s[1][sx][sy] == -1) 
  {
    printf("-1");
    return 0;
  }
  
  all = (1 << m) - 1;
  memset(f, 0x3f, sizeof (f));
  f[1][sx][sy][s[1][sx][sy]] = 0;
  for (int i = 2; i <= t; ++ i) 
  {
    for (int x = 1; x <= w; ++ x) 
	{
      for (int y = 1; y <= h; ++ y) 
	  {
        if (s[i][x][y] == -1) continue ;
        for (int j = 0; j <= 4; ++ j) 
		{
          int fx = x + dx[j], fy = y + dy[j];
          if (fx < 1 || fx > w || fy < 1 || fy > h) continue ;
          if (s[i - 1][fx][fy] == -1) continue ;
          for (int k = 0; k <= all; ++ k) 
		{
		swapmin(f[i][x][y][k | s[i][x][y]],f[i - 1][fx][fy][k]+(j>0));
		//j是否大于0表示为是否动了	
                }
        }
      }
    }
  }
  
  int ans = f[0][0][0][0];
  for (int x = 1; x <= w; ++ x) 
  {
    for (int y = 1; y <= h; ++ y) 
	{
      swapmin(ans, f[t][x][y][all]);
    }
  }
  printf("%d\n", ans < inf ? ans : -1);
  return 0;
}
posted @ 2020-11-04 19:46  SkyFairy  阅读(158)  评论(0编辑  收藏  举报