NOIP 模拟 18

NOIP 模拟 18

最近老是犯唐,这次也是。

T1

容易得到暴力代码:

namespace s1{
	bool sta[MAXN*MAXN];
	bool S[MAXN],T[MAXN];
	string s;
	int ans;
	int main(){
        cin>>n>>m;
		for(int i=1;i<=m;++i){
			cin>>s;
			memset(S,0,sizeof(bool)*(n+5));
			memset(T,0,sizeof(bool)*(n+5));
			for(int j=0;j<n;++j)
				switch(s[j]){
					case '0': break;
					case '1': T[j+1]=1;break;
					case '2': S[j+1]=1;break;
					default:  S[j+1]=T[j+1]=1;break;
				}
			for(int i=1;i<=n;++i)
				for(int j=i+1;j<=n;++j)	
					if((S[i]&&T[j])||(S[j]&&T[i]))
						sta[i*(n-1)+j]^=1;
		}
		for(int i=1;i<=n;++i)
			for(int j=i+1;j<=n;++j)
				if(sta[i*(n-1)+j])
					++ans;
		cout<<ans<<'\n';
		return 0;
	}
}

其实就是模拟题面的过程。我们发现 n104,是可以通过 O(n2) 的算法的。暴力程序真正的瓶颈在于每次都 n2 枚举而外面又套了一个 m,复杂度变成 O(mn2),无法通过。

发现每次的 n2 枚举的内部都只是简单的位运算 + 异或。不难想到 bitset 处理。具体地,将所有的 bool 全换成 bitset,然后:

  • Si=1,则 ansiansiTi
  • Ti=1,则 ansiansiSi
  • Si=1Ti=1,这时候会算重一次,只需 ansiansi(Si&Ti)

然后就没了。本质上就是把 n2 的枚举用 bitset 一步到位了。

for(int i=1,s,t;i<=n;++i){
    s=S[i],t=T[i];
    S[i]=T[i]=0;
    if(s) mp[i]^=T;
    if(t) mp[i]^=S;
    if(s&&t) mp[i]^=S&T;
}
for(int i=1;i<=n;++i)
    for(int j=i+1;j<=n;++j)
        if(mp[i][j])
            ++ans;
cout<<ans<<'\n';
return 0;

评价:bitset 使用不够熟练。考场上以为是什么神仙性质题,关键是还真推出来了只有 12 时的性质,于是就一直在错误的方向上摸索。

T2 序列

首先必须想到的一步转化:将所有操作转化为将总答案乘上一个数

那么对于操作一,转化为贡献 yax;对于操作二,转化为贡献 ax+yax;对于操作三,直接就是贡献 y

然而这样做会有一个问题,比如操作一,一旦之前有对 ax 操作,那么 ax 的值就会改变。也就是说,我们对一次操作的贡献无法静态算出。操作二也同理。

其实操作一导致的问题是好解决的,只需要取对 ax 的所有操作一中 y 的最大值,然后操作一就可以转化为操作二:把 ax 修改为 y 相当于加上 yax。但是,操作二造成的影响我们似乎真的不好处理。

考场上想到这就止步了。

但实际上,有一个显然的性质:对 ax 的加法操作中,我们肯定先操作 y 大的,然后再操作 y 小的。于是,当我们对 yi 计算贡献时,此时的 ax 实际上已经变为 ax+yj>yiyj。这只需要我们对于任意点的操作二进行从大到小排序,然后操作二的贡献我们就可以计算了。

于是我们将所有的操作都转化为了对总答案乘上一个数。从大到小排序,然后挨个计算并输出即可。

#include<bits/stdc++.h>
#define fw fwrite(obuf,p3-obuf,1,stdout)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define putchar(x) (p3-obuf<1<<20?(*p3++=(x)):(fw,p3=obuf,*p3++=(x)))
#define int long long
using namespace std;

char buf[1<<20],obuf[1<<20],*p1=buf,*p2=buf,*p3=obuf,str[20<<2];
int read(){
	int x=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x;
}
template<typename T>
void write(T x,char sf='\n'){
	if(x<0)putchar('-'),x=~x+1;
	int top=0;
	do str[top++]=x%10,x/=10;while(x);
	while(top)putchar(str[--top]+48);
	if(sf^'#')putchar(sf);
}
constexpr int MAXN=1e5+5,MOD=1e9+7;
int n,m,a[MAXN],ans=1;
vector<int>pls[MAXN];
int chg[MAXN];
vector<pair<double,int>>f;
int max(int a,int b){
	return a>b?a:b;
}
int power(int a,int b){
	int res=1;
	for(;b;a=a*a%MOD,b>>=1)if(b&1)res=res*a%MOD;
	return res; 
}

signed main(){
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	n=read(),m=read();
	for(int i=1;i<=n;++i) a[i]=read();
	for(int i=1,t,x,y;i<=m;++i){
		t=read(),x=read(),y=read();
		switch(t){
			case 1: chg[x]=max(chg[x],y);break;
			case 2: pls[x].emplace_back(y);break;
			default:f.emplace_back(y,y);
		}
	}
	for(int i=1,t;i<=n;++i){
		if(a[i]<chg[i]) pls[i].emplace_back(chg[i]-a[i]);
		sort(pls[i].begin(),pls[i].end(),greater<>());
		t=a[i];
		for(auto x:pls[i]){
			f.emplace_back(1.0*(t+x)/t,(t+x)*power(t,MOD-2)%MOD);
			t+=x;
		}
	}
	sort(f.begin(),f.end(),greater<>());
	for(int i=1;i<=n;++i) ans=ans*a[i]%MOD;
	for(auto x:f){
		write(ans,' ');
		ans=ans*x.second%MOD;
	}
	for(int i=f.size();i<=m;++i) write(ans,' ');
	return fw,0;
}

T3

std,输出 n2D90pts,五份 AC 代码面对一份 n=5 的数据有四个不同结果的神仙题。

T4 字符串

有一道基本相同的题目:CF610E

题目中 “插入字符” 操作不好维护,观察到 t 是一个排列,所以每个字母在 t 中仅会出现一次。故有引理:

任意字符 si 和 si+1 在一个循环节中,当且仅当 si 和 si+1 在 t 中相邻。

所以将每个字符按照在 t 中的位置编号,则原串转化为一个数字串,我们要求的就是数字串的逆序对数。区间修改显然线段树维护,在每个线段树节点上设 smi,j 为 [l,r] 中每一对相邻的字符中 (k) sk=isk+1=j 的数有多少个。复杂度 O(mk2logn)

#include<bits/stdc++.h>
#define lp p<<1
#define rp p<<1|1
#define mid ((s+t)>>1)
using namespace std;

constexpr int MAXN=2e5+5;
int n,m,k,tmp[10][10];
string S;
struct SegTree{
	int lc,rc,sm[10][10],lazy;
	SegTree(){
		lc=rc=lazy=0;
		memset(sm,0,sizeof(sm));
	}
	int*operator[](const int&x){
		return sm[x];
	}
}st[MAXN<<2];
void pushup(int p){
	for(int i=0;i<k;++i)
		for(int j=0;j<k;++j)	
			st[p][i][j]=st[lp][i][j]+st[rp][i][j];
	st[p].lc=st[lp].lc,st[p].rc=st[rp].rc;
	++st[p][st[lp].rc][st[rp].lc];
}
void build(int s,int t,int p){
	if(s==t){
		st[p].lc=st[p].rc=S[s]-'a';
		return;
	}
	build(s,mid,lp),build(mid+1,t,rp);
	pushup(p);
}
void pushlazy(int p,int c){
	for(int i=0;i<k;++i)
		for(int j=0;j<k;++j)
			tmp[i][j]=st[p][i][j];
	for(int i=0;i<k;++i)
		for(int j=0;j<k;++j)
			st[p][(i+c)%k][(j+c)%k]=tmp[i][j],tmp[i][j]=0;
	st[p].lc=(st[p].lc+c)%k;
	st[p].rc=(st[p].rc+c)%k;
	st[p].lazy=(st[p].lazy+c)%k;
}
void pushdown(int p){
	if(!st[p].lazy) return;
	pushlazy(lp,st[p].lazy);
	pushlazy(rp,st[p].lazy);
	st[p].lazy=0;
}
void mdf(int l,int r,int c,int s=1,int t=n,int p=1){
	if(l<=s&&t<=r) return pushlazy(p,c),void();
	pushdown(p);
	if(l<=mid) mdf(l,r,c,s,mid,lp);
	if(mid<r) mdf(l,r,c,mid+1,t,rp);
	pushup(p);
}
SegTree ask(int l,int r,int s=1,int t=n,int p=1){
	if(l<=s&&t<=r) return st[p];
	pushdown(p);
	if(l>mid) return ask(l,r,mid+1,t,rp);
	if(mid>=r) return ask(l,r,s,mid,lp);
	SegTree res1=ask(l,r,s,mid,lp),res2=ask(l,r,mid+1,t,rp),res;
	for(int i=0;i<k;++i)
		for(int j=0;j<k;++j)
			res[i][j]=res1[i][j]+res2[i][j];
	res.lc=res1.lc,res.rc=res2.rc;
	++res[res1.rc][res2.lc];
	return res;
}

int main(){
	freopen("d.in","r",stdin);
	freopen("d.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(nullptr),cout.tie(nullptr);
	cin>>n>>m>>k>>S;
	S=' '+S;
	build(1,n,1);
	while(m--){
		int op,l,r;
		cin>>op>>l>>r;
		switch(op){
			case 1:{
				int c;
				cin>>c;
				mdf(l,r,c);
				break;
			}default:{
				string s;
				cin>>s;
				int ans=0;
				SegTree res=ask(l,r);
				for(int i=0;i<k;++i)
					for(int j=0;j<=i;++j)
						ans+=res[s[i]-'a'][s[j]-'a'];
				cout<<ans+1<<'\n';
				break;
			}
		} 
	}
	return 0;
}
posted @   Laoshan_PLUS  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示