比赛传送门
给你一个n和d (1≤n≤109, 1≤d≤109) ,问是否存在自然数x,使得 x + ⌈ d x + 1 ⌉ ⩽ n x+\left \lceil \frac{d}{x+1}\right \rceil\leqslant n x+⌈x+1d⌉⩽n。存在输出YES,不存在输出NO。
节约推公式的时间,就写了一个o( n \sqrt{n} n)的暴力,从0到 d \sqrt{d} d枚举x,再判断式子是否成立即可。
有t(1≤t≤100) 组样例,每组样例给出两个整数A and B (1≤A,B≤109),问有多少对(a,b)满足1≤a≤A,1≤b≤B,且ab+a+b=conc(a,b)。其中conc(a,b)表示数字的字符连接,如conc(12,23)=1223。
设b的数字位数为w,则有ab+a+b=a⋅10w+b。整理得a⋅(10w-b-1)=0。因为a>0,得到b=10w-1,即9,99,999……剩下就不说了。
给定n和m (1≤n≤1000, 1≤m≤10)。问有多少对数组a和b满足:
1.数组a和b长度均为m。
2.a和b的元素均为1到n的整数。
3.对于i从1到m每一位,有ai ≤ bi成立。
4.数组a为非递减数组。
5.数组b为非递增数组。
答案模1e9+7。
容易发现,只要两数组的最后一位满足条件3,根据条件4和5,可以得到条件3必定成立。于是乎,我们两层循环枚举两数组最后一位,此时我们还需要知道,为了满足条件2,3,4,且最后一位给定,还有多少情况即可,可以用dp数组预处理,dp[i][j]表示长度为i的数组,上升空间为j的数组有多少种情况。就ok了。代码如下(m为1的特判纯属是因为发现过不了样例):
#include
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll dp[15][1005];
int main()
{
ll n,m;
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)dp[1][i]=i;
for(int i=2;i<=10;i++)
{
for(int j=1;j<=1000;j++)
{
for(int k=1;k<=j;k++)
{
dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
}
}
}
ll ans=0;
if(m!=1)
{
for(int b=1;b<=n;b++)
{
for(int a=1;a<=b;a++)
{
ans=(ans+dp[m-1][a]*dp[m-1][n-b+1])%mod;
}
}
}
else
{
ans=n*(n+1)/2%mod;
}
printf("%lld\n",ans);
return 0;
}
这题被hack了,很气,就不贴代码了。但思路应该没问题。
给定n个数组,每个数组长度为m。(1≤n≤3⋅105, 1≤m≤8)
选择其中两个数组ai和aj(可以是同一个),定义数组b为
要求这两个数组的其中一种选法,使得
输出选择的数组的下标。
首先想到二分这个最小值的最大值,然后考虑如何check。
如果存在两个数组,使得每一位上的最大值大于当前二分值,则check成功。
如果暴力枚举两数组,做法o(n²)显然过不了。
m只有8,考虑转化成二进制。
check当前二分值p的思路:把每个数组转化成二进制数,转化方式为,如果当前位的数大于等于p,则二进制下该位为1,否则为0。然后就会发现当存在两个数的或值为2m-1,则check成功。而m只有8,可能出现的二进制值不过256。所以可以用一个长度为256的数组记录这一个数是否出现,然后暴力两层循环判断是否存在两个数满足条件。
这是一道题目背景比模型化后的题意更好理解的题。就是消息列表里有n个人的消息,编号从1到n,在列表里从上到下的顺序也是1到n的一个排列。
当某个人给你发消息后,他在列表中的位置会瞬间蹦到第一个,而在他之前的人会往后移一格。
初始时列表里从上到下是1到n。
现在给你一个n和m (1≤n,m≤3⋅105)表示n个人, 在初始情况后又收到m条消息。
接下来m个数,表示收到的是谁的消息。
要求输出每个人的消息在列表中最靠前和最靠后的位置。
样例:
首先,容易处理出最靠前的位置。当这个人发过消息,那他最靠前的位置为1,否则为他初始位置。
现在思考最靠后的位置。当一个人出现后,他的位置变成第一,距离他下次变成第一前,这段时间,他的排名会往后掉发过消息的人种数个位置。
比如:1 3 2 2 1,1在变成第一后,往后掉了两个位置。
所以我们要找的就是,对于a来说,两个a之间相隔的数的种类数。
处理边界情况,神奇地想到在开头加上1到n的倒序表示初始化的情况。如样例变成5 4 3 2 1 3 5 1 4
处理起来会更方便。而像2这样只在初始状态时出现的,则从它记录到结尾的那个数即可。
于是乎问题变成了,n个区间,询问区间数的种类数。
用了莫队o(n n \sqrt{n} n)过了。
代码如下:
#include
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
int ans1[300005];
int ans2[300005];
int a[600005];
vector<int>v[300005];
typedef struct
{
int l,r,id,B;
}Query;
Query Q[1000005];
bool cmp(Query a,Query b)
{
if(a.B==b.B)return a.r<b.r;
else return a.B<b.B;
}
int mp[300005];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
int N=n+m;
int B=sqrt(N);
for(int i=1;i<=n;i++)a[i]=n-i+1,ans1[i]=i;
for(int i=n+1;i<=n+m;i++)scanf("%d",&a[i]),ans1[a[i]]=1;
for(int i=1;i<=N;i++)v[a[i]].push_back(i);
int qn=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<v[i].size();j++)
{
if(j==v[i].size()-1)Q[qn++]=Query{v[i][j],N,i,(v[i][j]-1)/B+1};
else Q[qn++]=Query{v[i][j],v[i][j+1],i,(v[i][j]-1)/B+1};
}
}
sort(Q,Q+qn,cmp);
int l=1,r=1,ans=1;
mp[a[1]]=1;
for(int i=0;i<qn;i++)
{
while(r<Q[i].r){r++;mp[a[r]]++;if(mp[a[r]]==1)ans++;}
while(l>Q[i].l){l--;mp[a[l]]++;if(mp[a[l]]==1)ans++;}
while(r>Q[i].r){mp[a[r]]--;if(mp[a[r]]==0)ans--;r--;}
while(l<Q[i].l){mp[a[l]]--;if(mp[a[l]]==0)ans--;l++;}
ans2[Q[i].id]=max(ans2[Q[i].id],ans);
}
for(int i=1;i<=n;i++)printf("%d %d\n",ans1[i],ans2[i]);
return 0;
}
没时间了,不会。