“再见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:
T1再Again:
对⻆线上的元素构成“等差差数列”,它的和是个三次多项式。 于是可以插值或者⼿玩即可。
到现在没“手玩出来”……
代码:
#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: T2建build:
向右的边肯定没⽤,每条向左连的边看成⼀个线段,则线段内的点可以互相到达。 于是可以⽤线段树维护,每次相当于区间覆盖,查询从 走能到达的最左端的点,可以在线段树上⼆ 分。时间复杂度 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 T3欧gull
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=0∑n(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=0∑n(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=0∑n(m+1i+1)(i+1)k−1
= ( 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=0∑n(m+1i)(i+1)k−1+i=0∑n(mi)(i+1)k−1)
这样我们成功让 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=0kai∑j=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:
T4哀sorrow:
平衡规划,分 k ≤ n k \le \sqrt n k≤n和 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 k≤n,对于每种 k k k都维护一个数据结构(用前缀和就行),查询的时候对于每个 k ≤ n k \leq \sqrt n k≤n都统计一下贡献就行。
时间复杂度 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;
}