2023CCPC女生专场题解 ABFGHKL(更新至7题)
碎碎念
场上5题铜牌中流,比赛前一晚跟队友打狂父打到一点,早上起来不出意外右手手腕肿了。。(凹了太多次追逐战)
复盘了一下发现确实也只有5题能在场上做出来,实在是时间不够+能力不足捏
目标大概补到8~9题
A
按题意模拟即可,但我写挂了,队友过的
B
一眼看出可以对 \(x\) 和 \(y\) 分别二分求解,设左边界为 $L = -1000 $ ,右边界为 $R = 1000 $ ,初始时固定 \(y\) 为 \(0\),询问 \(x = L\) 与 $ x = R$ ,得到左边界与目标点距离 \(d1\) ,右边界与目标点距离 \(d3\) ,若 \(d1 < d3\) 可知目标点必定在中点左侧,使 $ R = \frac{L+R}{2}$ ,再次询问。否则,可知目标点在中点或中点右侧,由于 \(int\) 是向下取整,需要使 \(L = \frac{L+R+1}{2}\) ,再次询问,否则可能导致死循环 。距离收敛后,固定 \(x\) ,对 \(y\) 进行相同询问即可。
注意询问得到的值为 \(0\) 时需立即结束程序,否则会导致 WRONG ANSWER。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define inf 0x3f3f3f3f
int x1,x2,x3;
int y1,y2,y3;
int nowx,nowy;
string s;
int ask(int x,int y)
{
	cout<<x-nowx<<' '<<y-nowy<<endl;
	fflush(stdout);
	nowx=x,nowy=y;
	int ans;
	cin>>ans;
	return ans;
}
void solve()
{
	int d0=0,d1=0,d3=0;
	int L=-1000,R=1000,MID;
	cin>>d0;
	if(d0==0) return;
	
	d1=ask(L,0);
	if(d1==0) return;
	
	d3=ask(R,0);
	if(d3==0) return;
	
	while(L<R)
	{
		
		if(d1<d3)
		{
			MID=(L+R)>>1;
			d3=ask(MID,nowy);
			if(d3==0) return;
			R=MID;
		}
		else if(d1>d3)
		{
			MID=(L+R+1)>>1;
			d1=ask(MID,nowy);
			if(d1==0) return;
			L=MID;
		}
		else
		{
			MID=(L+R)>>1;
			if(ask(MID,nowy)==0) return;
			break; 
		}
	}
	
	L=-1000,R=1000;
	d1=ask(nowx,L);
	if(d1==0) return;
	
	d3=ask(nowx,R);
	if(d3==0) return;
	while(L<R)
	{
		if(d1<d3)
		{
			MID=(L+R)>>1;
			d3=ask(nowx,MID);
			if(d3==0) return;
			R=MID;
			
		}
		else if(d1>d3)
		{
			MID=(L+R+1)>>1;
			d1=ask(nowx,MID);
			if(d1==0) return;
			L=MID;
		}
		else
		{
			MID=(L+R)>>1;
			if(ask(nowx,MID)==0) return;
			break; 
		}	
	}
}
main()
{
	solve();
}
F
构造方式很简单,按照元素递增的顺序倒着填即可。 若数组为 \(1\ 1\ 2\ 2\ 3\ 3\ 1\ 1\) ,可以构造出 \(4\ 3\ 6\ 5\ 8\ 7\ 2\ 1\) 。问题在于如何判断非法。有以下三个条件必须满足:
- \(f[i]\le i\)
 - \(f[i] \le max(f[j]+1)\ \ (1\le j<i)\)
 - \(f[i]\ge 1\)
 
场上读假题一次,判断非法错了若干次,到最后一小时才通过,令人感叹。。
G
可以用两个队列存双方宝可梦的信息,也是按照题意模拟即可。
场上没有看出来能量是属于每个宝可梦的,而不是属于双方角色的,又喜提3发罚时,乐
H
瞪了40min才看懂题意,其实就是求每个\(S_i\) 在 \(T_i\) 的所有子串的出现次数总和。
对于一个字符串 \(s\) ,假设它在字符串 \(t\) 中匹配到的位置是 \(i\) (这里指 \(s\) 中最后一个字母在 \(t\) 中的位置是 \(i\)),则 \(t\) 的所有子串中包含 \(s\) 的有 \((i-|s|+2)\times (|t|-i)\) 个,且题目需要求总的出现次数,因此即使 \(t\) 中包含多个 \(s\) ,也只需要将每一个匹配到 \(s\) 时的答案加和即可,不需要去重。由于题目是多个字符串在 \(t\) 中匹配,因此,需要构建AC自动机进行多模匹配。每次查询\(T_i\)的下一个字符,如果在AC自动机上匹配到 \(s\) ,将答案加上 \((i-|s|+2)\times (|t|-i)\),再沿着 $ fail$ 边接着统计答案。
交了喜提TLE,问题就在于每次匹配到一个字符串后总会沿着 \(fail\) 边暴力上跳到这个字符串的最大后缀处,导致了很多重复计算,如果数据够强,这种做法复杂度会被卡到 \(\sum|T| \cdot \sum|S|\)。想到我们每次相当于查询 \(T\) 的一个前缀,即 \(|T|-i\) 是固定的,如果能求出这个前缀所有后缀的总匹配次数 \(cnt\) 和总字符数 \(allsize\) ,则当前前缀的所有可匹配后缀的总答案就是 \((|T|-i)\cdot ((i+2)\cdot cnt-allsize)\) 。这样就能 \(O(1)\) 地统计答案。
可以采用类似前缀和的形式求,使用 \(vis\) 数组判断当前点是否已经被加和,如果没有,则对 \(fail\) 边dfs统计前缀和,并将路径上的 \(vis\) 数组设为true。否则,直接计算当前点的答案即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define inf 0x3f3f3f3f
const int N=1e6+100,mod=1e9+7;
int ch[N][26],ne[N],cnt[N],in[N],allsize[N],idx;
bool vis[N];
string s;
vector<int> vec[N];
void insert(string s)
{
	int p=0;
	for(int i=0;s[i];i++)
	{
		if(!ch[p][s[i]-'a']) ch[p][s[i]-'a']=++idx;
		p=ch[p][s[i]-'a'];
	}
	cnt[p]++;
	allsize[p]+=s.size();
}
void build()
{
	queue<int> q;
	for(int i=0;i<26;i++)
		if(ch[0][i]) q.push(ch[0][i]);
	while(!q.empty())
	{
		int u=q.front();q.pop();
		for(int i=0;i<26;i++)
		{
			int v=ch[u][i];
			if(v)
			{
				ne[v]=ch[ne[u]][i],q.push(v);
			}
			else ch[u][i]=ch[ne[u]][i];
		 } 
	}
}
void dfs(int j)
{
	if(vis[j]||j<=0) return;
	dfs(ne[j]);
	vis[j]=1;
	if(ne[j]>0)
	{
		allsize[j]+=allsize[ne[j]];
		cnt[j]+=cnt[ne[j]];
	}
}
long long query(string s)
{
	int ans=0;
	for(int k=0,i=0;s[k];k++)
	{
		i=ch[i][s[k]-'a'];
		dfs(i);
		ans=(ans+(long long)(s.size()-k)%mod*(cnt[i]*(k+2)-allsize[i])%mod)%mod;
		
	}
	return ans;
}
void solve()
{
	IOS;
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>s;
		insert(s);
	}
	build();
	for(int i=1;i<=m;i++)
	{
		cin>>s;
		cout<<query(s)<<'\n';
	}
	
}
main()
{
	solve();
}
K
狠狠地猜结论,输出 \(\frac{1}{n}\) 即可。
L
注意到数据范围很小,刚开始以为是什么差分约束题,一看数据范围直接暴力枚举 \(A_i\) 所有取值,对于每一种情况都统计评委得分,取最大值即可。
                    
                
                
            
        
浙公网安备 33010602011771号