“再见OI”模拟赛

“再见OI”模拟赛

下午在机房快乐划水的时候被抓去打模拟……然后爆零呜呜呜
赛时:
一看表下午4:00,这要是打模拟什么时候吃饭呀 无奈开始遍历
一看题目就知道是宋队出的……
T 1 : T1: T1:……应该是要找个规律?暴力构造矩阵然后统计能过60,然后特判k==1又能水20……吧
T 2 : T2: T2:……好像是图上的一些统计,没啥思路,放最后吧
T 3 : T3: T3:期望这块不太明白,但是好像就是累加然后除以m
T 4 : T4: T4:线段树上的操作,好像能切吧
此时过去30min
然后按顺序先开T1
开始就遇到一个巨大打击
我发现之前复杂度算错了,暴力只有20……
本着有分就行的心态开始写
大概20min写出来了
然后re……开始调
输出过程量的时候见鬼了
刚定义完的变量再输出就变成0了
调了半天也没有反应
眼看过去了1h,只好放下先去看T3
T3写了一半发现思路错了
期望完全不是那么一回事……
此时过去2h
然后出去吃了个饭,30min
回来先口胡了个T3
然后开T4
T4刚开始线段树建树忘记pushup……卡了挺长时间
然后样例依然不过
换了好几种修改方法还是不过……
只剩15min只好去写T2
T2邻接矩阵存图dfs没写完就到点了……
赛后:
果然爆零了
T1找出闹鬼原因了
我定义过程量为long long但是输出时为%d,导致出锅!!!
但是好像对了也是TLE
T4我题意理解错误导致处理修改区间出错,而且不写线段树复杂度比写还要优……
这样看还是太贪,直接暴力 O ( n 2 ) \Omicron(n^2) O(n2)跑不满能过60……
这次模拟赛大量时间浪费在看错题重构上
以后一定要看好题,想好再写!!!
题解:
T 1 再 A g a i n : T1再Again: T1Again

对⻆线上的元素构成“等差差数列”,它的和是个三次多项式。 于是可以插值或者⼿玩即可。

到现在没“手玩出来”……
代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MOD 998244353ll
#define RN 510

int mat[RN][RN];

int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};

static inline int getMotherFucker(int x)
{
	return (1ll * (2ll + 4ll * ((x >> 1ll) - 1ll)) % MOD * (x >> 1ll) + 1ll * (x & 1ll) * (1ll + 4ll * (x >> 1ll))) % MOD;
}

static inline int getFuckingShit(int x)
{
	return (1ll * (3ll + 2ll * (x - 1ll)) * x) % MOD;
}

static inline int getAPileOfShit(int x)
{
	return 1ll * x * (x + 1ll) % MOD * (2ll * x + 1) % MOD * 166374059ll % MOD;
}

static inline int getICantNameItAnymore(int x)
{
	return 1ll * (2ll + 2ll * (x - 1)) * x % MOD;
}

static inline int getIAmTiredOfDoingThis(int x)
{
	return 1ll * (3ll + 2ll * (x - 1)) * x % MOD;
}

static inline int getItsLate(int x)
{
	return (1ll * (6ll + 4ll * ((x >> 1ll) - 1ll)) * (x >> 1ll) % MOD + 1ll * (x & 1ll) * (3ll + 4ll * (x >> 1))) % MOD;
}

static inline int getUncleFucker(int x)
{
	return ((1ll * x * x % MOD * (x - 1) % MOD) - 1ll * getAPileOfShit(x) + MOD + getICantNameItAnymore((x + 1) >> 1) + getIAmTiredOfDoingThis(x >> 1)) % MOD;
}

int main(void)
{
	freopen("again.in", "r", stdin);
	freopen("again.out", "w", stdout);
	int t, ans = 0;
	scanf("%d", &t);
	for (int tt = 1; tt <= t; tt++)
	{
		int n, op, k;
		scanf("%d%d%d", &n, &op, &k);
		
		int asshole;
		if (op == 1)
		{
			int fuck_you = n - k;
			asshole = (1ll * getUncleFucker(fuck_you + 1) + 1ll * (k - 1) * getMotherFucker(fuck_you + 1)) % MOD;
		}
		else if (op == 2)
		{
			int fuck_you = n - k;
			if (k == 1)
				asshole = getUncleFucker(fuck_you + 1);
			if (k >= 2)
				asshole = (getUncleFucker(fuck_you + 2) - (n & 1) + MOD) % MOD;
			if (k >= 3)
				asshole = (1ll * asshole + 1ll * (k - 2) * getItsLate(fuck_you + 1)) % MOD;
		}
		ans ^= asshole;
	}
	printf("%d", ans);
	return 0;
}

T 2 建 b u i l d : T2建build: T2build

向右的边肯定没⽤,每条向左连的边看成⼀个线段,则线段内的点可以互相到达。 于是可以⽤线段树维护,每次相当于区间覆盖,查询从 走能到达的最左端的点,可以在线段树上⼆ 分。时间复杂度 O ( Q log ⁡ n ) \Omicron(Q\log n) O(Qlogn)

这题我没看见图是条链,导致完全没往线段树想……
其实正解也不是太难

#include<bits/stdc++.h>
#define ll long long
#define ls k<<1
#define rs k<<1|1
using namespace std;
const int maxn=5e5+5;
int n,q,tree[maxn<<2],pos[maxn<<2];
inline ll read()
{
	ll ret=0;char ch=' ',c=getchar();
	while(!(c<='9'&&c>='0')) ch=c,c=getchar();
	while(c<='9'&&c>='0') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
	return ch=='-'?-ret:ret;
}
void change(int k,int l,int r,int x,int y,int v)
{
	if(x<=l&&r<=y)
	{
		pos[k]+=v;
		if(pos[k]) tree[k]=r-l+1;
		else tree[k]=l==r?0:tree[ls]+tree[rs];
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) change(ls,l,mid,x,y,v);
	if(y>mid) change(rs,mid+1,r,x,y,v);
	tree[k]=pos[k]?r-l+1:tree[ls]+tree[rs];
}
int query(int k,int l,int r,int x)
{
	if(tree[k]==r-l+1) return l;
	if(l==r) return l+1;
	int mid=(l+r)>>1;
	if(x<=mid) return query(ls,l,mid,x);
	int v=query(rs,mid+1,r,x);
	return v==mid+1?query(ls,l,mid,x):v;
}
int main()
{
	n=read(),q=read();
	while(q--)
	{
		int op=read();
		if(op==1)
		{
			int l=read(),r=read();
			if(l<=r) continue;
//			swap(l,r);
			change(1,1,n,r+1,l,1);
		}
		else if(op==2)
		{
			int l=read(),r=read();
			if(l<=r) continue;
//			swap(l,r);
			change(1,1,n,r+1,l,-1);
		}
		else
		{
			int p=read();
			printf("%d\n",n-query(1,1,n,p)+2);
		}
	}
	return 0;
}
/*
7 8 
1 5 3 
3 4 
3 3 
1 7 4 
1 5 7 
3 6 
2 5 3 
3 4
*/

T 3 欧 g u l l T3欧gull T3gull

n n n m m m都减去 1 1 1
答案是 ∑ i = 0 n ( i m ) ( i + 1 ) k ( n + 1 m + 1 ) \dfrac{\sum_{i=0}^n \binom{i}{m}(i+1)^k}{\binom{n+1}{m+1}} (m+1n+1)i=0n(mi)(i+1)k
    ∑ i = 0 n ( i m ) ( i + 1 ) k \ \ \ \sum_{i=0}^n \binom{i}{m}(i+1)^k    i=0n(mi)(i+1)k
= ∑ i = 0 n ( i + 1 m + 1 ) m + 1 i + 1 ( i + 1 ) k = \sum_{i=0}^n \binom{i+1}{m+1}\frac{m+1}{i+1}(i+1)^k =i=0n(m+1i+1)i+1m+1(i+1)k
= ( m + 1 ) ∑ i = 0 n ( i + 1 m + 1 ) ( i + 1 ) k − 1 =(m+1)\sum_{i=0}^n \binom{i+1}{m+1}(i+1)^{k-1} =(m+1)i=0n(m+1i+1)(i+1)k1
= ( m + 1 ) ( ∑ i = 0 n ( i m + 1 ) ( i + 1 ) k − 1 + ∑ i = 0 n ( i m ) ( i + 1 ) k − 1 ) =(m+1)(\sum_{i=0}^n \binom{i}{m+1}(i+1)^{k-1}+\sum_{i=0}^n \binom{i}{m}(i+1)^{k-1}) =(m+1)(i=0n(m+1i)(i+1)k1+i=0n(mi)(i+1)k1)
这样我们成功让 k k k减掉了1,一直拆到 k = 0 k=0 k=0为止。
化为 ∑ i = 0 k a i ∑ j = 0 n ( j i + m ) = ∑ i = 0 n a i ( n + 1 m + i + 1 ) \sum_{i=0}^{k} a_i \sum_{j=0}^n \binom{j}{i+m}=\sum_{i=0}^n a_i\binom{n+1}{m+i+1} i=0kaij=0n(i+mj)=i=0nai(m+i+1n+1)
a i a_i ai为对答案的贡献次数,可以 O ( k 2 ) O(k^2) O(k2)递推计算。
卡空间的话可以别存阶乘的结果,把询问离线然后在求阶乘的过程中计算答案。
时间复杂度 O ( n + m + T k + k 2 ) O(n+m+Tk+k^2) O(n+m+Tk+k2)

过于阴间,等回头再做
T 4 哀 s o r r o w : T4哀sorrow: T4sorrow

平衡规划,分 k ≤ n k \le \sqrt n kn k > n k > \sqrt n k>n
k > n k> \sqrt n k>n ,暴力找出所有区间加上x,需要使用 O ( 1 ) O(1) O(1)区间加, O ( n ) O(\sqrt n) O(n )区间查询的数据结构,可以使用 O ( 1 ) O(1) O(1)单点加, O ( n ) O(\sqrt n) O(n )区间查询的分块然后类似树状数组区间操作改造一下。
k ≤ n k \leq \sqrt n kn ,对于每种 k k k都维护一个数据结构(用前缀和就行),查询的时候对于每个 k ≤ n k \leq \sqrt n kn 都统计一下贡献就行。
时间复杂度 O ( Q n ) O(Q \sqrt n) O(Qn )

分块不是noip内容,所以60分暴力真香……
60分暴力:

#include<bits/stdc++.h>
#define ll long long
#define int ll
using namespace std;
const int maxn=2e5+5;
int n,q,a[maxn],ans;
inline ll read()
{
	ll ret=0;char ch=' ',c=getchar();
	while(!(c<='9'&&c>='0')) ch=c,c=getchar();
	while(c<='9'&&c>='0') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
	return ch=='-'?-ret:ret;
}
signed main()
{
	n=read(),q=read();
	for(int i=1;i<=n;i++) a[i]=read();
	while(q--)
	{
		int op=read();
		if(op==1)
		{
			int k=read(),l=read(),r=read(),x=read();
			for(int i=0;i*k+l<=n;i++)
			{
				for(int j=l;k*i+j<=n&&j<=r;j++) a[k*i+j]+=x;
			}
		}
		else
		{
			ans=0;
			int l=read(),r=read();
			for(int i=l;i<=r;i++) ans+=a[i];
			printf("%lld\n",ans);
		}
	}
	return 0;
}

100分标程:

#include<bits/stdc++.h>
using namespace std;
#define fp(i,l,r) for(register int (i)=(l);(i)<=(r);++(i))
#define fd(i,l,r) for(register int (i)=(l);(i)>=(r);--(i))
#define fe(i,u) for(register int (i)=front[(u)];(i);(i)=e[(i)].next)
#define mem(a) memset((a),0,sizeof (a))
#define O(x) cerr<<#x<<':'<<x<<endl
#define int long long
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 wr(int x){
	if(x<0)putchar('-'),x=-x;
	if(x>=10)wr(x/10);
	putchar('0'+x%10);
}
const int MAXN=2e5+10;
int n,Q,qop[MAXN],qk[MAXN],ql[MAXN],qr[MAXN],qx[MAXN],s[1010],ans[MAXN],BB,B;
int a1[MAXN],a2[MAXN],s1[5010],s2[5010],bel[MAXN],L[MAXN];
inline void add(int p,int v){
	if(p>n)return;p=max(p,1ll);
	a1[p]+=v;s1[bel[p]]+=v;
	v*=p;a2[p]+=v;s2[bel[p]]+=v;
}
inline int ask(int p){
	if(!p)return 0;
	int ans=0;const int pp=p+1;
	fp(i,L[bel[p]],p)ans+=pp*a1[i]-a2[i];
	fd(i,bel[p]-1,1)ans+=pp*s1[i]-s2[i];
	return ans;
}
inline void solve(int k){
	mem(s);const int sk=k-1;
	fp(i,1,Q){
		if(qop[i]==1){
			if(qk[i]!=k)continue;
			int t=0,x=qx[i],l=ql[i],r=qr[i];
			fp(j,l,r)s[j]+=(t+=x);
			fp(j,r+1,sk)s[j]+=t;
		}
		else{
			int l=ql[i],r=qr[i],bl=l/k,br=r/k,pl=l%k,pr=r%k;
			if(bl==br)ans[i]+=s[pr]-(pl?s[pl-1]:0ll);
			else ans[i]+=(br-bl)*s[sk]-s[pl-1]+s[pr];
		}
	}
}
main(){
	freopen("sorrow.in","r",stdin);freopen("sorrow.out","w",stdout);
	n=read();Q=read();B=sqrt(n);BB=B;
	fp(i,1,n){
		bel[i]=(i-1)/B+1;
		if(!L[bel[i]])L[bel[i]]=i;
	}
	fp(i,1,n){
		int x=read();
		add(i,x);add(i+1,-x);
	}
	fp(i,1,Q){
		qop[i]=read();
		if(qop[i]==1){
			qk[i]=read();ql[i]=read();qr[i]=read();qx[i]=read();
			int k=qk[i],l=ql[i],r=qr[i],x=qx[i];
			if(k>BB){
				for(int j=0;j<=n;j+=k)
				add(j+l,x),add(j+r+1,-x);
			}
		}
		else{
			ql[i]=read();qr[i]=read();
			ans[i]=ask(qr[i])-ask(ql[i]-1);
		}
	}
	fp(i,1,BB)solve(i);
	fp(i,1,Q)if(qop[i]==2)wr(ans[i]),putchar('\n');
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值