首页 > 其他分享 >noip模拟17

noip模拟17

时间:2024-11-19 19:29:28浏览次数:1  
标签:17 noip int sum tot stk -- while 模拟

A 选取字符串

会 \(n^2\)。

直接枚举全部的 \(q,p\) 然后开一个二维的 bitset 去存一个数是否是某个数的前后缀。

选到两个 \(p,q\) 时把这两个数的 bitset 与起来,贡献是 \(\binom{count}{k}\)。

正解就是先用 kmp 去求出来全部的 border,然后用 border 关系建一棵树,这棵树上满足父亲是儿子的 border。根节点为空串。

然后就是树上选取 \(k\) 个点,求 lca 深度平方和。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+10,mod=998244353;
int f[N],g[N];
int ppow(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1) res=(res*a)%mod;
		a=(a*a)%mod,b>>=1;
	}return res; 
}
int n,k;
void init()
{
	f[0]=g[0]=1; 
	for(int i=1;i<=n+2;i++) f[i]=(f[i-1]*i)%mod;
	g[n+1]=ppow(f[n+1],mod-2);
	for(int i=n;i>=1;i--) g[i]=(g[i+1]*(i+1))%mod;
}
char c[N];
int nxt[N],dep[N],sum[N],cnt[N];
inline int getc(int n,int m)
{
	if(n<m) return 0;
	return f[n]*g[m]%mod*g[n-m]%mod;
}
signed main()
{
//	freopen("a2.in","r",stdin);
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>k>>(c+1);
	n=strlen(c+1);
	init();
//	cout<<n<<"\n";
	for(int i=2,j=0;i<=n;i++)
	{
		for(;j>0&&c[i]!=c[j+1];j=nxt[j]);
			if(c[i]==c[j+1])
			j++;nxt[i]=j;
	}
	for(int i=1;i<=n;i++)
		dep[i]=dep[nxt[i]]+1;
	for(int i=0;i<=n;i++)
		sum[i]++;
	for(int i=n;i>=1;i--)
		sum[nxt[i]]=sum[nxt[i]]+sum[i];
	for(int i=0;i<=n;i++)
		cnt[dep[i]]=(cnt[dep[i]]+getc(sum[i],k))%mod;
	int ans=0;
	for(int i=0;i<=n;i++)
		ans=(ans+1ll*cnt[i]*(2*i+1)%mod)%mod;
	printf("%d",ans);
	
	return 0;
}

B 取石子

其实没看懂题解。然后就粘过来了。。。

问题模型:取石子(NIM)游戏,要求每个人每次取的石子数不能超过上一个人刚刚取的,第一个人最
开始可以取不超过 \(K\) 个。
考虑策略:

  1. 如果 \(\sum_i a_i\) 是奇数,先手取 \(1\) 个必胜,因为每个人之后都只能取 \(1\) 个;

  2. 否则,先手最优一定取偶数个并且后面每个人能取偶数个都只会取偶数个(否则留给对手总和为奇
    数的情况,自己必败),所以可以递归到 \(K\gets \lfloor \frac{K}{2} \rfloor , a_i\gets \lfloor \frac{a_i}{2} \rfloor\)。

解得先手必胜当且仅当对于某个 \(t\le \log_2 K\),\(\sum{i=1}^n \lfloor \frac{a_i}{2^t}\rfloor\) 模 \(2\) 和
为 \(1\),也即 \(\mathop{\oplus}i a_i \not\equiv 0\pmod {2^{\lfloor \log_2 K\rfloor}}\)。

定义 \(\mathrm{lowbit}(x)\) 表示整除 \(x\) 的最大的 \(2\) 的幂。先手第一步能必胜的策略必定是取
\((2k+1)\cdot \mathrm{lowbit}(\mathop{\oplus}i a_i)\) 个。枚举取的堆,假设是第 \(i\) 堆,考虑枚举取
完之后另一个人面对的剩下的异或和的 \(\mathrm{lowbit}\),假设是 \(2^t\),那么先手取的个数为 \(a_i - (\mathop{\oplus}{j\ne i} a_j)\oplus 2^t \pmod {2^{t+1}}\)。由于必胜,后手不能取到 \(2^t\),于是自己
这次取的个数也必须小于 \(2^t\)。枚举 \(t\) 依次判断即可。注意处理 \(t=\infty\)(取完之后异或和归零)
的情况。

我的理解比较浅层,但是算一点:

  • 对于和为偶数的情况,所有选取的答案一定为偶数,奇数同理。

  • 对于奇数情况,必胜顺序一定是第一次先手选一个奇数,然后后手开始选偶数,即上述两种情况可以化为一起。

  • 对于偶数的选取情况,若 \(\displaystyle\sum \lfloor \frac{a_i}{2}\rfloor\) 为偶数,则选取的数一定是 \(4\) 的倍数,否则模 \(4\) 余 \(2\)。

但是我至今没有理解为什么取偶数个都会取偶数个

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,k;
inline int lowbit(int x)
{
	if(x==0) return 1e9;
	return x&-x;
}
const int N=5e4+6;
int *a;
signed main()
{
	freopen("nim.in","r",stdin);
	freopen("nim.out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int sum=0;
	cin>>n>>k;
	a=new int [n+2];
	for(int i=1;i<=n;i++)
	{
		cin>>a[i],sum^=a[i];
	}
	if(lowbit(sum)>k) return cout<<0,0;
	cout<<"1\n";
	for(int i=1;i<=n;i++)
	{
		int now=0,r=sum^a[i];
		while(114)
		{
			bool flag=0;
			for(int j=0;j<=30;j++)
			{
				if(now+(1<<j)>min(a[i],k)) continue;
				int kk=now+(1<<j);
				if(lowbit((a[i]-kk)^r)>kk)
				{
					now=kk,flag=1;break;
				}
			}
			if(!flag) break;
			cout<<i<<" "<<now<<"\n";
		}
	}
} 

C 均衡区间

首先,处理所有数左边第一个大于它的和小于它的数是显然的。贡献会加在这两个点 min 的外面。

然后就有一个跑的极快的假做法,虽然会被单调序列卡到 \(n^2\)。

如下。每次跳最大和最小的位置,然后更新答案。用补集的形式搞,所以不用处理合法的答案。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+6;
inline int read()
{
	register int s=0;
	register char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') s=(s<<1)+(s<<3)+(c^48),c=getchar();
	return s;
}
void write(int x)
{
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
int n,id,a[N],ans[2][N],mx[N],mi[N],st1[N],st2[N],top1,top2,b[N];
signed main()
{
	freopen("interval.in","r",stdin);
	freopen("interval.out","w",stdout);
	n=read(),id=read();
	if(id==2)
	{
		for(int i=1;i<=n;i++) putchar('0'),putchar(' ');
		putchar(10);
		for(int i=1;i<=n;i++) putchar('0'),putchar(' ');
		putchar(10);return 0;
	}
	for(int i=1;i<=n;i++) a[i]=read(),ans[0][i]=n-i+1,ans[1][i]=i;
	for(int i=n;i;i--)
	{
		while(top1&&a[st1[top1]]<a[i]) mx[st1[top1--]]=i;
		while(top2&&a[st2[top2]]>a[i]) mi[st2[top2--]]=i;
		st1[++top1]=i,st2[++top2]=i;
	}
	for(int i=1;i<=n;i++)
	{
		int j=min(mx[i],mi[i]);
		if(!j)j++;
		ans[1][i]-=i-j+1;
		--b[j],++b[i+1];
		int k=mx[i];
		while(k>=j) k=mx[k];
		while(k) ans[1][i]--,ans[0][k]--,k=mx[k];
		k=mi[i];
		while(k>=j) k=mi[k];
		while(k) ans[1][i]--,ans[0][k]--,k=mi[k];
	}
	for(int i=1;i<=n;i++)
	{
		b[i]+=b[i-1];
		write(ans[0][i]+b[i]),putchar(' ');
	}
	putchar(10);
	for(int i=1;i<=n;i++)
	{
		write(ans[1][i]),putchar(' ');
	}
}

正解其实就是把上面的处理看作二维数点问题,用树状数组解决。

代码来自 why
#include<bits/stdc++.h>
#define N 1000005
#define pb push_back
using namespace std;
int n,id;
int a[N];
int L[N],R[N],stk[N],tot;
vector<int> s[N],t[N];
struct BIT
{
	int c[N];
	void add(int x,int y) {while(x <= n){c[x] += y; x += (x & (-x));}}
	int sum(int x)
	{
		int res = 0;
		while(x) {res += c[x];x -= (x & (-x));}
		return res;
	}
	void cl() {memset(c,0,sizeof(c));}
}B;
int main()
{
	freopen("interval.in","r",stdin);
	freopen("interval.out","w",stdout);
	scanf("%d%d",&n,&id);
	for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
	for(int i = 1;i <= n;i++)
	{
		while(tot && a[i] <= a[stk[tot]]) tot--;
		L[i] = stk[tot] + 1;
		stk[++tot] = i;
	}
	tot = 0;
	for(int i = 1;i <= n;i++)
	{
		while(tot && a[i] >= a[stk[tot]]) tot--;
		L[i] = min(L[i],stk[tot] + 1);
		stk[++tot] = i;
	}
	tot = 0;stk[tot] = n + 1;
	for(int i = n;i >= 1;i--)
	{
		while(tot && a[i] >= a[stk[tot]]) tot--;
		R[i] = stk[tot] - 1;
		stk[++tot] = i;
	}
	tot = 0;stk[tot] = n + 1;
	for(int i = n;i >= 1;i--)
	{
		while(tot && a[i] <= a[stk[tot]]) tot--;
		R[i] = max(R[i],stk[tot] - 1);
		stk[++tot] = i;
	}
//	for(int i = 1;i <= n;i++) printf("%d %d\n",L[i],R[i]);
	int res = 0;
	for(int i = 1;i <= n;i++) s[L[i]].pb(i),t[i].pb(i);
	for(int i = 1;i <= n;i++)
	{
		for(int j : s[i]) B.add(j,1),res++;
		int buok = res - (B.sum(R[i]) - B.sum(i - 1));//总数减去合法部分 
		int ans = (n - R[i]) - buok;
		printf("%d ",ans);
		for(int j : t[i]) B.add(j,-1),res--;
	}
	printf("\n");
	res = 0;
	B.cl();
	for(int i = 1;i <= n;i++) s[i].clear(),t[i].clear();
	for(int i = 1;i <= n;i++) s[i].pb(i),t[R[i]].pb(i);
	for(int i = 1;i <= n;i++)
	{
		for(int j : s[i]) B.add(j,1),res++;
		int buok = res - (B.sum(i) - B.sum(L[i] - 1));
		int ans = (L[i] - 1) - buok;
		printf("%d ",ans);
		for(int j : t[i]) B.add(j,-1),res--;
	}
	return 0;
}

D 禁止套娃

标签:17,noip,int,sum,tot,stk,--,while,模拟
From: https://www.cnblogs.com/ccjjxx/p/18555366

相关文章

  • P1014 [NOIP1999 普及组] Cantor 表
    这道题需要我们按照Z形,给出第N项的值。按照Z形对表进行观察,我们可以对表中的数据进行一个分组如图,发现第一层有一个数,第二层有两个数,第三层有三个数,第n层有n个数,且奇数层的分母是从层数p开始数到1,也就是p,p-1,p-2,p-3........,分子是1数到p,偶像层与奇数层相反。那么知道这个......
  • 洛谷:P1008 [NOIP1998 普及组] 三连击
    这道题需要我们找出所有符合要求的数对,由于数据量不大,这里我们可以使用枚举的方法进行枚举,那么我们从最小的三位数100到最大数999进行遍历寻找,再对这三个数进行判断,判断这三个数的每一位是否由1-9这9个数组成,且每个数只出现一次。在判断这个地方我们可以用一个数组来进行计数,将......
  • [71] (多校联训) A层冲刺NOIP2024模拟赛24
    bydT3放道这种题有什么深意吗flowchartTB A(选取字符串) styleAcolor:#ffffff,fill:#00c0c0,stroke:#ffffff确实是签,但是一直在想组合意义,最后因为没提前处理逆元遗憾离场了,赛后看题解发现的确是往树上转化更简单点赛时的组合意义代码没过#include<bits/stdc++.h>us......
  • 【NOIP提高组】 统计数字
    【NOIP提高组】统计数字C语言代码C++代码Java代码Python代码......
  • 【NOIP普及组】记数问题
    【NOIP普及组】记数问题C语言代码C++代码Java代码Python代码......
  • 【NOIP普及组】 排座椅
    【NOIP普及组】排座椅C语言版本C++版本Java版本Python版本......
  • 使用java程序模拟电影案例(增删改查)
    publicstaticvoidmain(String[]args){//完成电影案例//1、创建电影对象:定义电影类//2、创建一个电影操作对象:专门负责对电影数据进行业务处理(上架、下架、查询、封杀)MovieServicemovieService=newMovieService();movi......
  • Docker部署ELK7.17.10
    一.安装前准备    需要准备elasticsearch_7.17.10,kibana_7.17.10,logstash7.17.10三个镜像,这里我用的离线镜像包elasticsearch_7.17.10.tar,kibana_7.17.10.tar,logstash7.17.101.先执行命令包导入镜像dockerload-ielasticsearch_7.17.10.tardockerload-ikiban......
  • 11.19 CW 模拟赛 T1.谁开了小号
    算法嗯,和赛时做法也是没有一点相似之处,寄!挂个\(\rm{TJ}\)题解下载对于暴力,显然可以用\(\rm{dfs}\)实现,这种\(\rm{dfs}\)我还没有见过大概有个想法,每次有两种选择,要么新开集合,要么从前面加一个进去,大概就这样,也比较简单,剪剪枝小数据包过的正解做......
  • 洛谷题单指南-二叉堆与树状数组-P5677 [GZOI2017] 配对统计
    原题链接:https://www.luogu.com.cn/problem/P5677题意解读:所谓好的配对,通过分析公式∣ax−ay∣≤∣ax−ai∣(i≠x),可以得知就是一个ax与其差的绝对值最小的形成的配对,在数轴上就是距离ax最近的点ay,配对是下标(x,y),给定若干个区间[l,r],每个区间的配对数*区间编号的累加。解题思路:......