今天的考试还是不太理想,很多暴力分都没拿到,希望明天能改进。
T1:星际旅行
一道氵题,先排个序,二分查找即可。
代码:
#include<bits/stdc++.h>
#define ll long long
#define db double
#define re register
#define cs const
#define N 200005
using namespace std;
inline int read()
{
int x=0,f=1;
char ch;
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
int n,s,l[N],x,ans,m;
int main()
{
freopen("dwar.in","r",stdin);
freopen("dwar.out","w",stdout);
n=read();
s=read();
for(re int i=1;i<=n;++i) l[i]=read();
sort(l+1,l+1+n);
l[n+1]=0x3f3f3f3f;
for(re int i=1;i<n;++i)
{
x=s-l[i];
if(x<=0) break;
m=upper_bound(l+1,l+n+1,x)-l-1;
if(m<=i) continue;
ans+=m-i;
}
printf("%d",ans);
}
T2:栖息于禅寺的妖蝶
一道推结论的计数问题,这道题竟然用杨辉三角推组合数,还有 n ² n² n²暴力得 70 p t s 70pts 70pts的做法,而我一开始就找错了方向。
正解:由于题目要求在 n n n个数中选出 m m m个数,而且必须是有序的,我们可以做一个巧妙的转化,设 b i = a i + i b_i=a_i+i bi=ai+i,则序列中的每个数都转化成了偶数,则问题就转化成了从 1 1 1到 n + m n+m n+m中选出 m m m个偶数,进一步就可转化成从 1 1 1到 ⌊ n + m 2 ⌋ \left\lfloor\frac{n+m}{2}\right\rfloor ⌊2n+m⌋选出 m m m个数,则答案就是 ( ⌊ n + m 2 ⌋ m ) \left(\begin{array}{c}{\left\lfloor\frac{n+m}{2}\right\rfloor} \\ { m}\end{array}\right) (⌊2n+m⌋m)
代码:
#include<bits/stdc++.h>
#define int long long
#define db double
#define re register
#define cs const
#define mod 998244353
#define N 1000005
using namespace std;
inline int read()
{
int x=0,f=1;
char ch;
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
int inv[N],pre[N],t,n,m;
void fuck(int x)
{
inv[0]=1;
inv[1]=1;
pre[0]=1;
for(re int i=2;i<=N;++i) inv[i]=(long long)inv[mod%i]*(mod-mod/i)%mod;
for(re int i=1;i<=N;++i) pre[i]=(long long)pre[i-1]*i%mod;
for(re int i=1;i<=N;++i) inv[i]=inv[i-1]*inv[i]%mod;
}
int work(int x,int y)
{
return (long long)pre[x]*inv[y]%mod*inv[x-y]%mod;
}
signed main()
{
t=read();
fuck(1e6);
while(t--)
{
n=read();
m=read();
printf("%lld\n",work((n+m)/2,m));
}
}
T3:八云蓝
我只能感性理解,这里复制粘贴下题解。
设询问区间为[x,y],对于每个线段树区间[l,r],我们统计有多少个[x,y]的子区间会影响到[l,r],
把所有区间分成4类:
1.[l,r]包含[x,y]
2.[l,r]和[x,y]真相交
3.[x,y]包含[l,r]
4.[x,y]和[l,r]没有交集
第4类对答案没有贡献不考虑
第1类显然[x,y]的所有子区间都会影响[l,r]
第2类其实分成左相交和右相交两类,不过没有本质区别
贡献可以用总区间数-和[l,r]不相交的区间数
第3类稍微麻烦一点,贡献是总区间数-和[l,r]不相交的区间数-完
全包含[l,r]父亲的区间数
第1,2类区间总共只有O(log n)个,可以暴力计算
考虑第3类区间,在线段树上是𝑂(log 𝑛)个子树,考虑预处理
写出贡献式子可以发现对于某个区间[l,r],若[x,y]包含[l,r],对答案的贡献可以写成 A x y + B x + C y + D Axy+Bx+Cy+D Axy+Bx+Cy+D的形式
直接对于每个点预处理出所有的系数然后求子树和
代码:
#include<bits/stdc++.h>
#define db double
#define re register
#define cs const
#define int long long
#define N 2000005
#define mid (l+r)/2
using namespace std;
char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
int x=0,f=1;
char ch;
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=nc();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=nc();
}
return f*x;
}
int sx[N],sy[N],sxy[N],b[N];
int n,q,opt,last,x,y;
void build(int k,int l,int r)
{
int ll=l+1;
int rr=r-1;
sx[k]+=rr;
sy[k]+=ll;
sxy[k]--;
b[k]-=ll*rr;
if(l==r) return;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
sx[k]+=sx[k<<1]+sx[k<<1|1];
sy[k]+=sy[k<<1]+sy[k<<1|1];
sxy[k]+=sxy[k<<1]+sxy[k<<1|1];
b[k]+=b[k<<1]+b[k<<1|1];
sx[k]-=r-l;
sy[k]+=r-l;
sx[k]-=2*rr;
sy[k]-=2*ll;
sxy[k]+=2;
b[k]+=(r-l+1)*(r-l+2)/2-1;
b[k]+=2*ll*rr;
b[k]+=l*(r-l);
b[k]-=r*(r-l);
}
int query(int k,int l,int r,int x,int y)
{
if(x<=l&&r<=y)
{
int ans=0;
ans+=x*sx[k]+y*sy[k]+sxy[k]*x*y+b[k];
return ans;
}
int ans=0;
if(l<=x&&y<=r)
{
ans=(y-x+1)*(y-x+2)/2;
if(x<=mid) ans+=query(k<<1,l,mid,x,y);
if(y>mid) ans+=query(k<<1|1,mid+1,r,x,y);
return ans;
}
if(x<l) ans+=(y-l+1)*(y-x+1+l-x+1)/2;
if(y>r) ans+=(r-x+1)*(y-x+1+y-r+1)/2;
if(x<=mid) ans+=query(k<<1,l,mid,x,y);
if(y>mid) ans+=query(k<<1|1,mid+1,r,x,y);
return ans;
}
void print(int n){
if(n>9) print(n/10);
putchar(n%10+48);
}
signed main()
{
n=read();
q=read();
opt=read();
build(1,1,n);
while(q--)
{
x=read();
y=read();
x=(x^(last*opt))%n+1;
y=(y^(last*opt))%n+1;
if(x>y) swap(x,y);
last=query(1,1,n,x,y);
print(last);
putchar('\n');
}
}