5.29测试
骗 分 大 赛
T1 X国的军队(army.cpp)
题目大意
你分析得出,攻占一个据点\(i\),为了稳定己方士兵士气,至少需要\(B_i\)个士兵参战,战后将会有\(A_i\)个士兵阵亡。
由于不停地调谴,可用的士兵已经不多了,于是在一个据点参战且未阵亡的士兵可能会参加别的据点的战斗。
你需要计算出攻破这\(n\)个据点所需要的最少的士兵数目。
\(T\)组数据
对于前\(20\%\)的数据\(n≤9\)
对于前\(40\%\)的数据\(n≤1000\)
对于\(100\%\)的数据\(n≤100000\),\(T≤10\),\(1≤A_≤B_≤1000000000\)
Sol
不会,写了个20pts的暴力就爪巴了
但是票池会,确实是神
啊是贪心啊那没事了
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x*f;
}
struct jd
{
int a,b,x;
bool operator<(const jd &X)const
{
return x>X.x;
}
}e[100010];
int T,n;
signed main()
{
freopen("army.in","r",stdin);
freopen("army.out","w",stdout);
T=read();
while(T--)
{
n=read();
for(int i=1;i<=n;i++)
{
e[i].a=read(),e[i].b=read();e[i].x=e[i].b-e[i].a;
}
sort(e+1,e+n+1);
int ans=0,now=0;
for(int i=1;i<=n;i++)
{
if(now<e[i].b)
{
ans+=e[i].b-now;
now=e[i].b;
}
now-=e[i].a;
}
printf("%lld\n",ans);
}
return 0;
}
T2 排列组合(pc.cpp)
题目大意
\(T\)组数据,每次给定\(n\),请求出下式的值,对\(10^9+7\)取模:
对于\(30\%\)的数据,\(T≤500 , n≤10000\)
对于\(100\%\)的数据,\(T≤100000 , n≤1000000\)
Sol
学了半天组合数学,全部木大。
认真推式子我没推出来。所以写了个暴力看前\(20\)项的规律。
1 2 6 20 70 2552 924 3432 12870 48620 184756 ......
先是质因数分解,分出来没啥规律,放弃。
然后是差分后找规律,没找出来,放弃。
想了一下还是推一下式子吧。由定理可得
那么
不难得出两个平方项在对每个\(C_n^i\)计算之后求和的结果都是\(f(\ n\ -\ 1\ )\)
由此大概可以得出\(f(n)=2\ *\ f(n\ -\ 1)\ +\ ?\)
于是我又照着两倍差分了一遍,还是啥也没有。数据如下:
0 2 8 30 112 420 1584 6006
上了个厕所,发了一会儿神,然后就找到规律了。
\(0=1x0/1\)
\(2=2x2/2\)
\(8=6x4/3\)
\(30=20x6/4\)
\(...\)
\(g(i)=f(i)*(2n-2)/n\)
式子有了\(f(n)=(4n-2)*f(n-1)\)
剩下的就是数论基操
注意是多测,所以预处理然后\(O(1)\)回答即可
啊是范德蒙德卷积标准款啊我sb了
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=1000000007;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x*f;
}
int ans[1000010],inv[1000010];
inline void pre()
{
inv[1]=1;
for(int i=2;i<=1000000;i++)
{
inv[i]=(-(inv[p%i]*(p/i))%p+p)%p;
}
ans[0]=1;
for(int i=1;i<=1000000;i++)
{
ans[i]=ans[i-1]*inv[i]%p*(4*i-2)%p;
}
return;
}
signed main()
{
freopen("pc.in","r",stdin);
freopen("pc.out","w",stdout);
pre();
int T=read();
while(T--)
{
int x=read();
printf("%lld\n",ans[x]);
}
return 0;
}
T3 Dinner(dinner.cpp)
题目大意
一共\(N\)个人坐在坐在圆桌旁。
服务员拿来了\(M\)份菜单。第\(i\)个人阅读菜单并点出
自己喜欢的菜需要花费时间\(T_i\)。
当一个人点完菜之后,就会把菜单传到他右手边的第一个人。
\(M\)份菜单是同时发出的,每个菜单只能同时被一个人阅读。
清儿希望知道如何分发菜单,才能让点餐的总时间花费最少呢?
对于\(20\%\)的数据,\(n≤100\),\(m≤100\)
对于\(60\%\)的数据,\(n≤10000\),\(m≤100\)
对于\(100\%\)的数据,\(n≤50000,T_i≤600\),\(m≤3000\)
Sol
\(lsw\)神写的标答树上倍增,然而我不会
两次二分:第一次二分答案,第二次用前缀和优化后二分寻找下一个发菜单的位置
\(60\)分,学校霸主一样的方法\(100\)分。
可能是用了以下的优化
. 破环成链的时候可以先试一下前面算出来的答案能否通过,不能就直接跳过,保证了运算不冗余。
. 跳到k的位置直接结束(我没听懂啥意思,学校霸主说的)
我的\(60pts\)代码
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x*f;
}
int sum[100010],t[100010],n,m,ans=1e9,L,R;
inline int next(int st,int lim,int beg)
{
int l=st+1,r=beg+n;
while(l<r)
{
int mid=(l+r+1)>>1;
if(sum[mid-1]-sum[st-1]>lim)r=mid-1;
else l=mid;
}
return l;
}
inline int check(int lim,int st)
{
int le=sum[n],ti=m;
for(int i=st;le>0&&ti;)
{
ti--;
int to=next(i,lim,st);
le-=sum[to-1]-sum[i-1];
i=to;
}
if(le>0)return 0;
return 1;
}
inline int ord(int st)
{
int l=L,r=R;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid,st))r=mid;
else l=mid+1;
}
return l;
}
int main()
{
freopen("dinner.in","r",stdin);
freopen("dinner.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;i++)
{
t[i]=t[i+n]=read();
sum[i]=sum[i-1]+t[i];
L=max(L,t[i]);
}
R=sum[n];
for(int i=n+1;i<=n+n;i++)sum[i]=sum[i-1]+t[i];
for(int i=1;i<=n;i++)
{
ans=min(ans,ord(i));
}
printf("%d",ans);
return 0;
}
学校霸主的\(100pts\)的代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
//20pts
inline int read(){
int x=0;bool flag=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')flag=1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
return flag?-x:x;
}
void file(){
freopen("dinner.in","r",stdin);
freopen("dinner.out","w",stdout);
}
#define N 100005
int a[N];
int n,m;
int mx;
int sum,s[N];
bool check(int x){
int pr=0,sf=n+1;
while(pr+a[sf]<=x){
pr+=a[sf];
int now=sf-1;
int lsw=0;
while(now<sf+n-1){ //枚举l
int l=now+1,r=sf+n-1,ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(s[mid]-s[now]<=x)ans=mid,l=mid+1;
else r=mid-1;
}
now=ans;
lsw++;
if(lsw>m)break;
}
if(now==sf+n-1&&lsw<=m)return 1;
sf--;
}
return 0;
}
int main(void){
file();
n=read(),m=read();
for(int i=1;i<=n;i++)a[i]=read(),a[i+n]=a[i],mx=max(mx,a[i]),sum+=a[i];
for(int i=1;i<=2*n;i++)s[i]=s[i-1]+a[i];
int l=mx,r=sum,ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)){
r=mid-1;
ans=mid;
}
else l=mid+1;
}
int x=1;
if(n>105)for(int i=1;i<=1000000000;i++)x+=1;
printf("%d\n",ans);
return 0;
}
T4 回文(pal.cpp)
题目大意
他打算搞出一个数据结构,能够快速求出这个字符串下标为\([l,r]\)的子串的回文子串个数
对于\(20\%\)的数据,保证\(|S| , T≤500\)
对于\(40\%\)的数据,保证\(|S| , T ≤5000\)
对于\(100\%\)的数据,保证\(|S| ≤5000 , T≤100000\)
Sol
考试的时候写的\(manacher\),但是挂了。非常板子拿\(40pts\)。
正解在学校霸主代码的帮助下搞出来了——区间\(dp\)
\(dp[i][j]\)表示区间\([i,j]\)的答案,布尔类数组\(b[i][j]\)表示子串\([i,j]\)是否是回文
首先预处理\(dp[i][i]\)和\(dp[i-1][i]\)以及\(v[i][i]\)和\(v[i-1][i]\)
然后第一维枚举区间宽度,第二维枚举区间起点
转移方程\(dp[i][j]=dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]\)
注意如果当前枚举的区间是回文,则\(dp[i][j]++\)
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x*f;
}
int dp[5010][5010],len,T,l,r;
bool b[5010][5010];
char s[5010];
inline bool check(int L,int R)
{
if(b[L][R])return true;
if(s[L]==s[R]&&check(L+1,R-1))return true;
return false;
}
inline void f()
{
for(int i=1;i<=len;i++)
{
dp[i][i]=1;b[i][i]=true;dp[i-1][i]=2;
if(s[i]==s[i-1])
{
b[i-1][i]=true;dp[i-1][i]=dp[i-1][i-1]+dp[i][i]+1;
}
}
for(int k=3;k<=len;k++)
{
int i=1,j=k;
for(;j<=len;i++,j++)
{
dp[i][j]=dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1];
if(check(i,j))dp[i][j]++;
}
}
}
int main()
{
freopen("pal.in","r",stdin);
freopen("pal.out","w",stdout);
scanf("%s",s+1);
len=strlen(s+1);
f();
T=read();
while(T--)
{
l=read();r=read();
printf("%d\n",dp[l][r]);
}
return 0;
}

浙公网安备 33010602011771号