蒟蒻的省选模拟自闭祭

上周省里发了一套模拟题
本来弱省历届省选都是什么高精度、dp原题、模拟之类的balabala的水题
今年这个模拟题突然就做自闭了。。。。


D1T1 余弦

题目描述

给定长度为NN的实数序列 Ai(1iN)A_i(1\leq i\leq N), 你需要在数列上进行两类操作:

  1. lirl\leq i\leq r中的每个AiA_i加上实数vv
  2. lirl\leq i\leq rcos(Ai)cos(A_i)的和 。

说明

对于 100%的数据,1 ≤ N M ≤ 200000

T1还是比较水的,推推式子线段树维护

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long lt;
typedef double dd;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=400010;
int T,n,m;
dd a[maxn];
dd Scos[maxn<<2],Ssin[maxn<<2],add[maxn<<2];

void pushup(int p)
{
    int lc=p<<1,rc=p<<1|1;
    Scos[p]=Scos[lc]+Scos[rc];
    Ssin[p]=Ssin[lc]+Ssin[rc];
}

void build(int s,int t,int p)
{
    add[p]=0.0;
    if(s==t){ 
        Scos[p]=cos(a[s]); 
        Ssin[p]=sin(a[s]);
        return;
    }
    int mid=s+t>>1;
    build(s,mid,p<<1);build(mid+1,t,p<<1|1);
    pushup(p);
}

void pushd(int s,int t,int mid,int p)
{
    if(add[p]==0) return;
    int lc=p<<1,rc=p<<1|1;
    add[lc]+=add[p]; add[rc]+=add[p];
    
    dd tc=Scos[lc],ts=Ssin[lc];
    Scos[lc]+=(cos(add[p])-1.0)*tc-sin(add[p])*ts;
    Ssin[lc]+=(cos(add[p])-1.0)*ts+sin(add[p])*tc;
    
    tc=Scos[rc],ts=Ssin[rc];
    Scos[rc]+=(cos(add[p])-1.0)*tc-sin(add[p])*ts;
    Ssin[rc]+=(cos(add[p])-1.0)*ts+sin(add[p])*tc;
    
    add[p]=0;
}

void update(int ll,int rr,int s,int t,int p,dd val)
{
    if(ll<=s&&t<=rr){
        dd tc=Scos[p],ts=Ssin[p];
        Scos[p]+=(cos(val)-1.0)*tc-sin(val)*ts;
        Ssin[p]+=(cos(val)-1.0)*ts+sin(val)*tc;
        add[p]+=val;
        return;
    }
    int mid=s+t>>1;
    pushd(s,t,mid,p);
    if(ll<=mid) update(ll,rr,s,mid,p<<1,val);
    if(rr>mid) update(ll,rr,mid+1,t,p<<1|1,val);
    pushup(p);
}

dd qsum(int ll,int rr,int s,int t,int p)
{
    if(ll<=s&&t<=rr) return Scos[p];
    int mid=s+t>>1; dd res=0;
    pushd(s,t,mid,p);
    if(ll<=mid) res+=qsum(ll,rr,s,mid,p<<1);
    if(rr>mid) res+=qsum(ll,rr,mid+1,t,p<<1|1);
    return res;
}

int main()
{
    T=read();
    for(int cs=1;cs<=T;++cs)
    {
        n=read();m=read();
    	for(int i=1;i<=n;++i)
    	scanf("%lf",&a[i]);
    	
    	build(1,n,1);
    	printf("Case #%d:\n",cs);
    	while(m--)
    	{
    		int opt=read(),ll=read(),rr=read();
    		if(opt==1)
    		{
    			dd x; scanf("%lf",&x);
    			update(ll,rr,1,n,1,x);
            }
            else if(opt==2) 
            printf("%.3lf\n",qsum(ll,rr,1,n,1));
        }
    }
    return 0;
}

D1T2 续一秒

题目描述

蛤先生是一位著名的科学家,不久前他得到了一种名叫“熵破坏者”的药剂。

这种药保存在NN个瓶子中,其中第ith(1iN)ith(1\leq i\leq N)个瓶子盛有ViV_i升药剂。为了让“熵破坏者”发挥魔力,他必须把所有保存在瓶子中的药剂混合在一起。由于混合药剂是一件危险的工作,蛤先生取出了他珍藏已久的大容器,并打算把药剂以整瓶为单位,全部倒进大容器中。

大容器的容量可以看作无穷大。若大容器中已有pp升药剂,则再倒入qq升药剂后,大容器中剩余药剂的体积是pq|p-q|(神奇的混合方法!)。最初,大容器是空的,蛤先生可以以任意一种顺序把每瓶药剂倒入大容器。

最终,若大容器中还剩余RR升药剂,则蛤先生就可以使用一次魔法,把自己的生命周期续RR秒。请帮蛤先生求出可能混合成的最大的RR,即蛤先生这次最多可以续几秒。

输入格式

输入包含多组数据,在数据开头有一个整数 T(T≤5),表示组数。对于每组数据:第一行一个整数 N,第二行 N 个整数Vi。

说明

对于 20%的数据,1N101\leq N\leq 10Vi20|V_i|\leq 20
对于 60%的数据,1N501\leq N\leq 50Vi100|V_i|\leq 100
对于 100%的数据,1N2001\leq N\leq 200, Vi500|V_i|\leq 500

啥啥啥这都啥???背包???贪心???
算了不会。。。写个模拟退火好了
最后提交的时候只有80pts,还算比较欧了吧

//模拟退火
#include<iostream>
#include<cstdio>
#include<cmath>
#include<set>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long lt;
typedef double dd;
#define T 100
#define eps 1e-8
#define delta 0.997

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=1010;
int Q,n,m;
int a[maxn],ans;

int calc()
{
    int p=0;
    for(int i=1;i<=n;++i)
    p=abs(p-a[i]);
    return p;
}

void SA()
{
    dd t=T;
    while(t>eps)
    {
        int x=0,y=0;
        while(x==y) x=rand()%n+1,y=rand()%n+1;
        swap(a[x],a[y]);
        int tt=calc(),dE=tt-ans;
        if(dE>=0) ans=tt;
        else if( exp(dE/t) > (dd)rand()/(dd)RAND_MAX );
        else swap(a[x],a[y]);
        t*=delta;
    }
}

int main()
{
    Q=read(); srand(19260817);
    for(int cs=1;cs<=Q;++cs)
    {
        n=read();
        for(int i=1;i<=n;++i) a[i]=read();
        if(n==1){ printf("Case #%d: %d\n",cs,abs(a[1])); continue;}
        
        ans=calc();
        for(int i=1;i<=3;++i) SA();
        printf("Case #%d: %d\n",cs,ans);
    }
    return 0;
}

std给的好像确实是贪心,负数都最后加没问题,但正数的贪心真的没看懂,我好菜=_=

//std
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[210], f[100010], T, N, n, m, ans, sum;

int main()
{
    freopen("second.in","r",stdin);
    freopen("second.out","w",stdout);
    cin >> T;
    for (int t = 1; t <= T; t++)
    {
        cin >> n; N += n;
        ans = m = sum = 0;
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            if(a[i] > 0) sum += a[i];
        }
        for (int j = 0; j <= sum; j++) f[j] = 0;
        f[0] = 1;
        sort(a + 1, a + n + 1);
        for (int i = 1; i < n; i++)
        {
            if (a[i] <= 0)
            {
                ans -= a[i];
                continue;
            }
            for (int j = m; j >= 0; j--)
                f[j + a[i]] |= f[j];
            m += a[i];
        }
        int delta = m + 1;
        for (int j = 0; j <= m; j++)
            if (f[j] && abs(j - (m - j)) < delta)
                delta = abs(j - (m - j));
        if (a[n] > 0) ans += a[n] - delta; else ans -= a[n];
        printf("Case #%d: %d\n", t, ans);
    }
    //cout << N << endl;
}

D1T3 松鼠与坚果

题目描述

小取酒是小 Cat Rainbow 的好朋友,它们一起到森林里玩。这片森林里住着一群松鼠。小取酒很喜欢松鼠,所以它对其中三只松鼠的活动习性进行了详细的跟踪观察。

在这三只松鼠中,第一只喜欢吃榛子,第二只喜欢吃松果,第三只喜欢吃栗子。有一天,它们收集了 N 个篮子的食物,其中每个篮子里恰好装了 1 个榛子、1 个松果和 1 个栗子,但是不同的篮子里的食物有不同的美味度。

现在它们邀请小取酒把这 N 个篮子分成三堆,第 1 只松鼠会吃掉第 1 堆每个篮子里的榛子,它获得的美味度就是第 1 堆篮子里美味度最大的榛子的美味度。同样地,第 2 只松鼠会吃掉第 2 堆篮子里的松果,获得第 2 堆篮子里美味度最大的松果的美味度。第 3 只松鼠会吃掉第 3 堆篮子里的栗子,获得第 3 堆篮子里美味度最大的栗子的美味度。

可是,小取酒还没有吃东西!于是它打算采取适当的方式把篮子分成三堆,使得三只松鼠获得的美味度之和最小。当然,它也有可能不分给某些松鼠食物,也就是说空堆是允许的,它甚至可以把这 N 个篮子全部分给某一只松鼠。没有获得篮子的松鼠获得的美味度是 0。

输入格式

第一行一个数 N,表示 N 个篮子。
接下来 N 行,每行三个数 ai, bi, ci 表示一个篮子里榛子、松果和栗子的美味度。

说明

对于 20%的数据,1 ≤ n ≤ 1000。
对于另外 30%的数据,1 ≤ ai ≤ 10。
对于 100%的数据,1 ≤ n ≤ 100000, 1 ≤ ai, bi, ci ≤ 100000000。

自闭了。。又不会
写到这题的时候已经花了两个小时,省选总时长4h,模拟我给自己限定3h
想了20分钟没有思路,部分分也不会拿,于是心力憔悴的我开始写暴搜。。最后10pts。。。

std又没有看懂,应该也是贪心吧,变量名全是a,b,c,d毒瘤的hin

//std
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
using namespace std;
set<int> b;
map<int,int> c;
struct rec{int x,y,z;}a[100010];
int n,m,p,d[100010];
set<pair<int,int> > f;
priority_queue<pair<int,int> > q;

bool operator <(rec a,rec b)
{
    return a.x>b.x;
}

int main()
{
    freopen("squirrel.in","r",stdin);
    freopen("squirrel.out","w",stdout); 
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
        b.insert(a[i].y);
    }
    sort(a+1,a+n+1);
    for(set<int>::iterator it=b.begin();it!=b.end();it++)
        c[*it]=++p,d[p]=*it;
    int ans=a[1].x;
    f.insert(make_pair(p+1,0));
    f.insert(make_pair(0,1<<30));
    q.push(make_pair(0,0));
    for(int i=1;i<=n+1;i++)
    {
        int j=c[a[i].y];
        set<pair<int,int> >::iterator it,it2;
        while(q.size())
        {
            it=f.lower_bound(make_pair(q.top().second+1,0));
            if(-q.top().first==d[q.top().second]+it->second) break;
            q.pop();
        }
        ans=min(ans,a[i].x-q.top().first);
        if(i==n+1) break;
        it=f.lower_bound(make_pair(j,a[i].z));
        if(a[i].z<=it->second) continue;
        q.push(make_pair(-a[i].y-it->second,j));
        it--;
        while(it->second<=a[i].z)
        {
            it2=it--;
            f.erase(it2);
        }
        f.insert(make_pair(j,a[i].z));
        q.push(make_pair(-d[it->first]-a[i].z,it->first));
    }
    cout<<ans<<endl;
}

最后D1模拟愉快地只有190pts。。。
大概加回那一个小时最后一题还能再多骗点分吧


D2T1传销组织

题目描述

传销组织 GPLT 的宗旨是“有志者事竟成”,他们最近在执行一项宏伟的 N 人计划,以构建科学有效的情报网。换句话说,GPLT 组织希望组建一个由 N 个人和若干单向私有电话线构成的情报网,并使得情报网满足一系列要求。这些要求分成两类:

① 从第 a 个人通过 1 条或多条电话线可以联系到第 b 个人。
② 从第 a 个人通过 1 条或多条电话线不能联系到第 b 个人。

现在 GPLT 组织的首脑 gluo 请你帮忙给出一个满足所有要求的情报网,或者告诉他这样的情报网是不可能存在的。

输入格式

第一行 1 个整数 N,表示情报网的人数。
第二行 1 个整数 M,表示①类要求的个数,接下来 M 行每行 2 个整数 a, b
第 M+3 行 1 个整数 T,表示②类要求的个数,接下来 T 行每行 2 个整数 a, b

输出格式

若不存在这样的情报网,输出 NO。否则在第一行输出 YES,在第二行输出情报网中话线的数量 P,接下来 P 行每行 2 个整数描述电话线。由于资源有限,要求 P<=N+M+T。

说明

对于 20%的数据,1 ≤ N ≤ 100。
对于 60%的数据,1 ≤ N ≤ 25000。
对于 100%的数据,1 ≤ N, M, T ≤ 100000,1 ≤ a, b ≤ N,a≠b,要求输出的 P<=N+M+T。

怎么第一题就自闭了。。。。又是部分分都没拿
D2写完看了std前半思路和我一样,后面的维护方法是什么鬼???压位+分块???

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string>
#include <bitset>
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
using namespace std;
typedef long long ll;
const int N=100002;
int ver[N],Next[N],head[N],c[N],dfn[N],low[N],s[N],ins[N],du[N],a[N],b[N],deg[N];
int n,m,t,qu,tot,num,p;
vector<int> go[N];
bitset<8192> d[N];

template<typename T> inline void R(T &x) {
	char ch = getchar(); x = 0;
	for (; ch < '0'; ch = getchar());
	for (; ch >= '0'; ch = getchar()) x = x * 10 + ch - '0'; 
}

void add(int x,int y)
{
	ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
}

void tarjan(int x)
{
	dfn[x]=low[x]=++num;
	s[++p]=x,ins[x]=1;
	for(int i=head[x];i;i=Next[i])
	{
		int y=ver[i];
		if(!dfn[y])
		{
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(ins[y])
			low[x]=min(low[x],dfn[y]);
	}
	if(low[x]==dfn[x])
	{
		t++; int y;
		do{y=s[p--]; ins[y]=0; c[y]=t;}while(x!=y);
	}
}

bool topsort(int l,int r)
{
	//cout<<l<<' '<<r<<endl;
	queue<int> q;	
	for(int i=1;i<=t;i++)
	{
		deg[i]=du[i];
		d[i].reset();
	}
	for(int i=1;i<=t;i++)
		if(!deg[i]) q.push(i);
	while(q.size())
	{
		int x=q.front(); q.pop();
		if(x>=l&&x<=r) d[x][x-l]=1;
		for(int i=0;i<go[x].size();i++)
		{
			int y=go[x][i];
			d[y]|=d[x];
			if(--deg[y]==0) q.push(y);
		}
	}
	for(int i=1;i<=qu;i++)
		if(b[i]>=l&&b[i]<=r&&d[a[i]][b[i]-l]==1)
			return 0;
	return 1;
}

int main() {
	freopen("gplt.in","r",stdin);
	freopen("gplt.out","w",stdout);
	while(cin>>n>>m)
	{
		memset(head,0,sizeof(head));
		memset(dfn,0,sizeof(dfn));
		memset(c,0,sizeof(c));
		memset(du,0,sizeof(du));
		tot=num=p=t=0;
		for(int i=1;i<=n;i++) go[i].clear();
		for(int i=1;i<=m;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			add(x,y);
		}
		for(int i=1;i<=n;i++)
			if(!dfn[i]) tarjan(i);
		for(int x=1;x<=n;x++)
			for(int i=head[x];i;i=Next[i])
			{
				int y=ver[i];
				if(c[x]==c[y]) continue;
				go[c[y]].push_back(c[x]);
				du[c[x]]++;
			}
		cin>>qu;
		for(int i=1;i<=qu;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			a[i]=c[x],b[i]=c[y];
		}
		bool flag=0;
		for(int l=1;l<=t;l+=8192)//这啥???压位??分块??
		{
			int r=min(t,l+8191);
			if(!topsort(l,r)) {flag=1; break;}
		}
		if(flag) {puts("NO"); continue;}
		puts("YES");
		cout<<m<<endl;
		for(int x=1;x<=n;x++)
			for(int i=head[x];i;i=Next[i])
			{
				int y=ver[i];
				printf("%d %d\n",x,y);
			}
	}
	return 0;
}

D2T2 Zhizhang Snake

题目描述

Zhizhang Snake 是一个新型物种,它的身体是由 N 个点和 N-1 条线段构成的折线,其中第 i 个点的坐标为(xi, yi),折线不会自交。Zhizhang Snake 可以平移或旋转自己的身体,但是在移动过程中,身体形状不能发生任何改变(即构成它身体的每条线段的长度和它们之间的夹角都保持不变),否则它就会挂掉……

直线 y=0 是一堵墙,坐标(0,0)处开有一个洞,洞与蛇身的宽度都是一个可以忽略不计的小量。现在 Zhizhang Snake 完全处于墙的上方(y>0),它想知道它自己的整个身体能否活着穿过墙洞,到达墙的下方(y<0)。

蒟蒻的省选模拟自闭祭

输入格式

每个测试点包含 5 组数据,以 EOF 结尾,对于每组数据:
第一行有 1 个整数 N,表示 Zhizhang Snake 折线顶点的个数。
接下来 N 行每行 2 个整数(xi, yi),描述这条折线。折线不会自交,折线上任意三个顶点都不共线。

输出格式

对于每组数据,输出 Possible 或 Impossible,表示 Zhizhang Snake 能否到达墙的下方。

说明

对于 20%的数据,1 ≤ N ≤ 10
对于 60%的数据,1 ≤ N ≤ 1000。
对于 100%的数据,1 ≤ N ≤ 100000, 0 ≤ xi ≤10^9,1 ≤ yi ≤10^9, 折线不会自交,折线上任意三个顶点都不共线。

看到这题内心十分复杂。。。心态崩了。。计算几何只会求裸凸包。。。直接跳了
std又没看懂系列

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string>
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
using namespace std;
struct poi { long double x, y; };
poi a[100010];
set<pair<long double, int> > f;
int n;
long double eps = 1e-10;

long double mul(poi a, poi b, poi c)
{
	return (b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x);
}

void insert(poi ct, int i)
{
	set<pair<long double, int> >::iterator it, l, r;
	long double z = atan2(a[i].y - ct.y, a[i].x - ct.x);
	l = r = f.lower_bound(make_pair(z, i));
	if (r == f.end()) r = f.begin();
	if (l == f.begin()) l = --f.end(); else l--;
	if (mul(a[i], a[l->second], a[r->second]) < -eps)
	{
		if (l == f.begin()) it = --f.end(); else it = l, it--;
		while (f.size() > 2 && mul(a[i], a[l->second], a[it->second]) > -eps)
		{
			f.erase(l);
			l = it;
			if (l == f.begin()) it = --f.end(); else it = l, it--;
		}
		if (r == --f.end()) it = f.begin(); else it = r, it++;
		while (f.size() > 2 && mul(a[i], a[r->second], a[it->second]) < eps)
		{
			f.erase(r);
			r = it;
			if (r == --f.end()) it = f.begin(); else it = r, it++;
		}
		f.insert(make_pair(z, i));
	}
}

long double dist(poi a, poi b)
{
	return sqrt((b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y));
}

bool in(poi ct, poi p)
{
	long double th = atan2(p.y - ct.y, p.x - ct.x);
	set<pair<long double, int> >::iterator it, l, r;
	l = r = f.lower_bound(make_pair(th, n + 1));
	if (r == f.end()) r = f.begin();
	if (l == f.begin()) l = --f.end(); else l--;
	if (fabs(th - l->first) < eps)
		return dist(ct, p) + eps < dist(ct, a[l->second]);
	if (fabs(th - r->first) < eps)
		return dist(ct, p) + eps < dist(ct, a[r->second]);
	return mul(a[l->second], p, a[r->second]) < -eps;
}

bool check1()
{
	poi ct;
	ct.x = ct.y = 0;
	for (int i = 1; i <= 3; i++)
		ct.x += a[i].x, ct.y += a[i].y;
	ct.x /= 3, ct.y /= 3;
	f.clear();
	for (int i = 1; i <= 3; i++)
		f.insert(make_pair(atan2(a[i].y - ct.y, a[i].x - ct.x), i));
	for (int i = 4; i <= n; i++)
	{
		insert(ct, i);
		if (in(ct, a[i])) return 0;
	}
	return 1;
}

bool check2()
{
	poi ct;
	ct.x = ct.y = 0;
	for (int i = n; i > n - 3; i--)
		ct.x += a[i].x, ct.y += a[i].y;
	ct.x /= 3, ct.y /= 3;
	f.clear();
	for (int i = n; i > n - 3; i--)
		f.insert(make_pair(atan2(a[i].y - ct.y, a[i].x - ct.x), i));
	for (int i = n - 3; i; i--)
	{
		insert(ct, i);
		if (in(ct, a[i])) return 0;
	}
	return 1;
}

int main() {
	freopen("snake.in","r",stdin);
	freopen("snake.out","w",stdout); 
	while (cin >> n)
	{
		for (int i = 1; i <= n; i++)
		{
			double x, y;
			scanf("%lf%lf", &x, &y);
			a[i].x = x, a[i].y = y;
		}
		if (n < 4)
		{
			puts("Possible");
			continue;
		}
		if (!check1() || !check2())
		{
			puts("Impossible");
			continue;
		}
		puts("Possible");
	}
	return 0;
}

D2T3 分子配对

题目描述

一次偶然的机会,Haibara 得到了一粒 APTX4869 毒药。为了研究其成分以制作解药,Haibara 在显微镜下对 APTX4869 进行了详细的观察。APTX4869 的分子结构是一条链,链上的每个部位宽窄不一,形成了锯齿的形状。

Haibara 在这条分子链上选择了 N 个具有代表性的节点,并用一个整数表示每个节点处的宽度。

APTX4869 进入活生物体后,分子链将会在体内环境下带动机能细胞一起折叠、扭曲,引起生物体各系统的紊乱而致死。但在特殊的情况下,APTX4869 的分子链重叠时,部分锯齿刚好整齐地“咬合”在一起,避免了紊乱的发生,却造成了生物体细胞全面变小或恢复为以前某时刻的状态。

我们说 APTX4869 分子链的 n 个代表节点中,第 la~ra 个节点与 lb~rb个节点是“咬合”的,当且仅当节点区间[la,ra],[lb,rb]满足下列条件:

  1. [la,ra]与[lb,rb]不重叠,即 la ≤ ra < lb ≤ rb 或 lb ≤ rb < la ≤ ra;
  2. [la,ra]与[lb,rb]的长度相等,即 ra-la = rb-lb;
  3. 对应节点的高度和相等,即对于任意的 0 ≤ i ≤ ra - la,有 w[la+i]+w[lb+i]=w[la]+w[lb],其中 w[x]表示第 x (1 ≤x ≤n) 个节点处的宽度。

现在 Haibara 给出 m 段区间,请你帮她统计一下对于每段区间,有多少段区间与它是“咬合”的。

输入格式

第一行一个整数 n。
第二行包含 n 个整数,第 i 个数表示 w[i],即第 i 个节点处的宽度。
第三行一个整数 m。接下来 m 行,每行有两个整数 l,r,表示询问有多少段区间与[l,r]是“咬合”的。

说明

对于 20%的数据,1<=n,m<=100。
对于另 30%的数据,询问区间的长度不超过 10。
对于 100%的数据,1<=n,m<=100000,1<=w[i]<=10^9,1<=l<=r<=n。

本来到这里已经打算自暴自弃不测了然而突然发现T3反而比较简单???十分钟左右有了思路
只要两端区间差分序列正好是对应的相反数就是"咬合的"
差分序列取反接在原差分序列后面求后缀数组,st表维护height,主席树查找区间个数
思路想的挺快但码了好久。。。差不多一小时50分钟吧才调好。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
typedef long long lt;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int inf=2e9;
const int maxn=1000010;
int n,m,Q;
int val[maxn],a[maxn];
int b[maxn],pos[maxn],cnt;
int rak[maxn],tp[maxn];
int sa[maxn],tax[maxn],height[maxn];
int mi[maxn][30];
int rt[maxn],size[maxn<<5],ch[maxn<<5][2],sz;

void rsort()
{
    for(int i=0;i<=m;++i) tax[i]=0;
    for(int i=1;i<=n;++i) tax[rak[i]]++;
    for(int i=1;i<=m;++i) tax[i]+=tax[i-1];
    for(int i=n;i>=1;--i) sa[tax[rak[tp[i]]]--]=tp[i];
}

void ssort()
{
    m=cnt;
    for(int i=1;i<=n;++i)
    rak[i]=a[i],tp[i]=i;
    
    rsort(); 
    for(int k=1;k<=n;k<<=1)
    {
        int p=0;
    	for(int i=n-k+1;i<=n;++i) tp[++p]=i;
        for(int i=1;i<=n;++i) if(sa[i]>k) tp[++p]=sa[i]-k;
        
    	rsort(); 
    	swap(rak,tp);
    	rak[sa[1]]=p=1;
    	for(int i=2;i<=n;++i)
        rak[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+k]==tp[sa[i-1]+k])?p:++p;
        if(p>=n)break;
    	m=p;
    }
}

void getH()
{
    int k=0;
    for(int i=1;i<=n;++i)
    {
        if(k) k--;
        int j=sa[rak[i]-1];
        while(a[i+k]==a[j+k]) k++;
        height[rak[i]]=k;
    }
}

void RMQ()
{
    for(int i=1;i<=n;++i) mi[i][0]=height[i];
    for(int j=1;(1<<j)<=n;++j)
    for(int i=1;i+(1<<j)-1<=n;++i)
    mi[i][j]=min(mi[i][j-1],mi[i+(1<<j-1)][j-1]);
}

int qmin(int ll,int rr)
{
    int k=0;
    while((1<<k+1)<=rr-ll+1) k++;
    return min(mi[ll][k],mi[rr-(1<<k)+1][k]);
}

int update(int pre,int ll,int rr,int x)
{
    int tt=++sz; size[tt]=size[pre]+1;
    ch[tt][0]=ch[pre][0]; ch[tt][1]=ch[pre][1];
    int mid=ll+rr>>1;
    if(ll<rr)
    {
        if(x<=mid) ch[tt][0]=update(ch[pre][0],ll,mid,x);
        else ch[tt][1]=update(ch[pre][1],mid+1,rr,x);
    }
    return tt;
}

int query(int u,int v,int ll,int rr,int s,int t)
{
    if(ll<=s&&t<=rr) return size[v]-size[u];
    int res=0,mid=s+t>>1;
    if(ll<=mid) res+=query(ch[u][0],ch[v][0],ll,rr,s,mid);
    if(rr>mid) res+=query(ch[u][1],ch[v][1],ll,rr,mid+1,t);
    return res;
}

int qpos1(int ll,int rr,int x)
{
    int L=ll,R=rr,res=rr;
    while(L<R){
        int mid=L+R>>1;
        if(qmin(mid+1,rr)>=x) R=mid,res=mid;
        else L=mid+1;
    }
    return res;
}

int qpos2(int ll,int rr,int x)
{
    int L=ll,R=rr,res=ll;
    while(L<R){
        int mid=L+R>>1;
        if(qmin(ll+1,mid)>=x) L=mid+1,res=mid;
        else R=mid;
    }
    return res;
}

lt solve(int ll,int rr)
{
    int len=rr-ll+1;
    int L=qpos1(1,rak[ll],len);
    int R=qpos2(rak[ll],n,len);

    lt res1=query(rt[L-1],rt[R],rr+(n>>1)+2,n,1,n);
    lt res2=query(rt[L-1],rt[R],(n>>1)+1,ll+(n>>1)-len-1,1,n);
    return res1+res2;
}

int main()
{
    n=read();
    for(int i=1;i<=n;++i) val[i]=read();
    for(int i=1;i<n;++i)
    {
        a[i]=b[i]=val[i+1]-val[i];
        a[i+n]=b[i+n]=-(val[i+1]-val[i]);
    }
    a[n]=a[n<<1]=b[n]=b[n<<1]=inf; n=n<<1;
    
    sort(b+1,b+1+n);
    for(int i=1;i<=n;++i)
    if(i==1||b[i]!=b[i-1])
    pos[++cnt]=b[i];
    
    for(int i=1;i<=n;++i)
    a[i]=lower_bound(pos+1,pos+1+cnt,a[i])-pos;
    
    ssort(); getH(); RMQ();
    for(int i=1;i<=n;++i)
    rt[i]=update(rt[i-1],1,n,sa[i]);
    
    Q=read();
    while(Q--)
    {
        int ll=read(),rr=read();
        if(ll==rr) printf("%lld\n",(n>>1)-1);
        else printf("%lld\n",solve(ll,rr-1));
    }
    return 0;
}

最后D2只做了两个半小时就自暴自弃了,100pts滚粗吧
完了去看std也确实证明我再想两个小时也没用。。。

于是蒟蒻省选模拟290pts自闭滚粗。。。


后来拿到的题解。。。看完之后觉得自己已经拿到能拿的分了。。我好菜:)
蒟蒻的省选模拟自闭祭
蒟蒻的省选模拟自闭祭