Day1 考试

考试

T1

给定 \(a\), \(b\), \(c\)
\(a^b\ mod\ c\)
1.2 输入格式
一行,空格隔开的三个数字,分别表示 \(a\); \(b\); \(c\)
1.3 输出格式
一个数字,表示答案。
1.4 样例输入
2 3 3
1.5 样例输出

2

1.6 数据规模及约定
对于 30% 的数据, 1 ≤ \(a\); \(b\); \(c\)\(10^6\)
对于 60% 的数据, 1 ≤ \(a\); \(b\); \(c\)\(10^9\)
对于 100% 的数据, 1 ≤ \(a; b; c\)\(10^{15}\)

显然快速幂搞定,但是\(a,b\)\(long\ long\)范围,所以再来个龟速乘

一点补充:

如果我们要读高精度且有知道mod,我们可以边读边取模.

(鸣谢毒瘤Water_lift)

\(Code\)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int inf=2147483647;
inline ll read()
{
	char ch=getchar();
    ll x=0;bool f=0;//然鹅我这里看着1e15写成了int(甚至不知道自己为什么一共三个数要用快读)
    while(ch<'0'||ch>'9')
    {
    	if(ch=='-') f=1;
    	ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<3)+(x<<1)+(ch^48);
		ch=getchar();
	}
	return f?-x:x;
}
ll a,b,c;
ll mul(ll x,ll y)
{
	ll r=0;
	while(y)
	{
		if(y&1) r=(r+x)%c;
		x=x*2%c;
		y>>=1;
	}
	return r;
}
ll ksm()
{
	ll r=1;
	while(b)
	{
		if(b&1) r=mul(r,a);
		a=mul(a,a);
		b>>=1;
	}
	return r;
}
int main()
{
	freopen("nine.in","r",stdin);
	freopen("nine.out","w",stdout);
    a=read();b=read();c=read();
    printf("%lld",ksm());
}
T2

2.1 问题描述
给定长度为 \(n\) 的序列 \(a_i\)
求满足以下要求的 (i; j) 数对的数量。
\(i<j\)
\(a_i > a_j\)
\((a_i + a_j)\equiv0\mod3\)
2.2 输入格式
第一行一个整数 \(n\)
接下来一行,空格隔开的 \(n\) 个整数,表示 \(a_i\)
2.3 输出格式
输出包含一个数字,表示答案。
2.4 样例输入
5
2 3 1 1 4
2.5 样例输出
2
2.6 样例解释
(1,3), (1,4) 满足要求

2.7 数据规模及约定
对于前 30% 的数据,$ n$ ≤ 2000。
对于前 60% 的数据, \(n\) ≤ 4 × \(10^4\)
对于前 100% 的数据, 1 ≤ \(n\)\(10^5\), 0 ≤ \(a_i\)\(10^6\)

显然前两个条件是逆序对

归并做法:

归并排序可求逆序对个数

处理条件3:若\((a+b)\ mod\ 3=0\),则\(a \mod 3+b\ mod\ 3=0\),所以我们可以每次都维护一个后缀和,计算\(mod\ 3=0\)\(mod\ 3=1\)\(mod\ 3=2\)的数的个数

\(Code\ (from\ std)\)

#include<cstdio
#include <cstring>
#include <algorithm>
#include <iostream>

using namespace std;
typedef long long LL;

int n;
int a[100005];
int b[100005];
int sum[100005][3];

LL ans;

void merge_sort(int l,int r){
    if(l==r) return;
    int mid=(l+r)/2;
    merge_sort(l,mid);
    merge_sort(mid+1,r);
    for(int i=l;i<=r;i++) b[i]=a[i];
    for(int i=mid;i>=l;i--){
        if(i<mid) for(int j=0;j<3;j++) sum[i][j] = sum[i+1][j];//由于是从mid直接覆盖过来所以不用memset
        sum[i][b[i]%3]++;
    }
    int i = l;
    int j = mid+1;
    int k = l;

    while(i<=mid && j<=r){
        if(b[i]<=b[j]){
            a[k++]=b[i];
            i++;
        }else{
            // solve
            ans += sum[i][(3-b[j]%3)%3];
            a[k++]=b[j];
            j++;
        }
    }
    while(i<=mid) a[k++] = b[i],i++;
    while(j<=r) a[k++] = b[j],j++;//由于i已经到了mid,所以不需要再统计逆序对了
}

int main(){
	
	freopen("teen.in", "r", stdin);
	freopen("teen.out", "w", stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    merge_sort(1,n);
    printf("%lld\n",ans);
    return 0;
}

树状数组做法:

可以开3棵树状数组,分别维护\(mod\ 3=0,mod\ 3=1,mod\ 3=2\)的数。因为树状数组我不会求后缀和,所以树状数组里记录的值\(d\)\(10^6+1\)减原值,这样就要找使得\((d+a)\ mod\ 3=1\)\(a\)

\(Code\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int inf=2147483647;
inline ll read()
{
	char ch=getchar();
    int x=0;bool f=0;
    while(ch<'0'||ch>'9')
    {
    	if(ch=='-') f=1;
    	ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<3)+(x<<1)+(ch^48);
		ch=getchar();
	}
	return f?-x:x;
}
int n,a[100009],b[1000009][3];
ll ans;
int hv[1000009];
const int all=(int)1e6+1;
void add(int d)
{
	int md=d%3;hv[d]++;
	while(md<0) md=(md+3)%3;
	for(;d<=all;d+=(d&(-d)))
	 b[d][md]++;
}
ll sum(int d,int md)
{
	ll r=0,dd=d;
	while(md<0) md=(md+3)%3;
	for(;d;d-=(d&(-d)))
	 r+=b[d][md];
	if(dd%3==md&&hv[dd]) r-=hv[dd];
	return r; 
}
int main()
{
	freopen("teen.in","r",stdin);
	freopen("teen.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	for(int i=1;i<=n;i++)
	{
		int d=all-a[i];
		ans+=sum(d,(1-d+3)%3);
	    add(d);
	} 
	printf("%lld",ans);
}
T3

3.1 问题描述
给定一个长度为 n 的序列 ai, 和一个数字 k。
如果一个区间 a[l : r] 满足以下条件,称其为 \(\gamma\) 区间。
\(l < r\)
\(max_{l≤i≤r}a_i - min_{l≤i≤r}a_i ≤ k\)
询问所有 \(\gamma\) 区间的数字和。因为这个值很大,所以输出答案模 \(10^9 + 7\)的余数就好了。
因输入量较大,推荐使用以下函数进行读入。

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

3.2 输入格式
第一行空格隔开的两个整数 n; k 。
接下来一行,空格隔开的 n 个整数,表示 ai。
3.3 输出格式
输出只有一个整数,表示答案。
3.4 样例输入
6 1
2 3 1 1 1 4
3.5 样例输出
12
3.6 样例解释
共四个 \(γ\) 区间。
a[1..2] = 2,3
a[3..5] = 1,1,1
a[3..4] = 1,1
a[4..5] = 1,1
3.7 数据规模及约定
对于前 30% 的数据, n ≤ 103 。
对于前 60% 的数据, n ≤ 105。
对于前 100% 的数据, 1 ≤ n ≤ 1000000, 0 ≤ ai; k ≤ 109。

先只考虑\(\gamma\)区间的数量

我们发现如果区间\([l,r]\)是合法的,那么\([l,r'],l<r'<r\)一定是个合法区间,如果\([l,r]\)不合法,那么\([l,r''],r''>r\)一定不合法。对于每个\(l\),我们想找一个最靠后的\(r\)

所以我们可以在枚举左端点的同时二分右端点

二分+\(check\):

注意如果区间\([l,n]\)是合法的,那么二分出来的右端点会是\(n-1\)而不是\(n\),所以要先判一遍。

复杂度\(O(nlogn)\)

我们想想能不能\(O(n)\)找到\(γ\)区间。

我们枚举\(l\)每次\(+1\),那么新的\(r\)和原来的\(r\)肯定有联系。

\(l'\)为新的\(l\),\(r'\)为新的\(r\),则\([l',r]\)肯定是合法区间,那么\(r'\)一定不会比\(r\)小,所以可以直接移动\(r\),每次都判断一下是否合法。

这样移动\(l,r\)总体是\(O(n)\)

但是\(check\)还是\(logn\)的,所以怎样才能\(O(1)\ check\)?

由于\(l,r\)都是不断往后移,不会往前,所以可以用单调队列。开两个单调队列分别维护\(Max\)\(Min\),\(check\)一次\(O(1)\),总复杂度\(O(n)\)

求数字和:
第一个区间可以暴力求出来。

考虑转移。

\(l->l+1,r\)不变:减去\(len\times a[l]\),其中\(len\)是区间\([l,r]\)的长度

\(l\)不变,\(r->r+1\):加上\(sum[r+1]-sum[l-1]\),也就是加上区间\([l,r+1]\)的区间和

\(Code\) \((from\ std)\)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>

using namespace std;

typedef long long LL;
int n,k;
int a[1000005];
LL ans;
const LL mod=1e9+7;

struct dddl{
	int q[1000005];
	int h,t;
	void push(int x){
		while(h!=t && a[q[t-1]]>=a[x]) t--;
		q[t++] = x;
	}
	void pop(int x){
		if(q[h] == x) h++;
	}
	int ask(){
		return a[q[h]];
	}
}mn;

struct dddl2{
	int q[1000005];
	int h,t;
	void push(int x){
		while(h!=t && a[q[t-1]]<=a[x]) t--;
		q[t++] = x;
	}
	void pop(int x){
		if(q[h] == x) h++;
	}
	int ask(){
		return a[q[h]];
	}
}mx;

int main(){
	
	freopen("nineteen.in", "r", stdin);
	freopen("nineteen.out", "w", stdout);
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int first = 1;
	LL sum=0;
	LL sum_suf=0;
	for(int i=1;i<=n;i++){
		mn.push(i);
		mx.push(i);
		sum = (sum+a[i])%mod;
		sum_suf = ((sum_suf)%mod+a[i]*(i-first+1)%mod)%mod;
		while(mx.ask() - mn.ask()>k){
			mx.pop(first);
			mn.pop(first);
			sum_suf = (sum_suf-sum+mod)%mod;
			sum = (sum-a[first]%mod+mod)%mod;
			first++;
		}
		ans += sum_suf;
		// printf("# %d %d %lld %lld\n",first, i, sum, sum_suf);
	}
	for(int i=1;i<=n;i++) ans = (ans-a[i]%mod+mod)%mod;
	cout<<ans<<endl;

	return 0;
}
posted @ 2020-01-18 18:06  千载煜  阅读(168)  评论(0编辑  收藏  举报