noip模擬測試38
阿新 • • 發佈:2021-08-13
這兩天考試覺得自己不在狀態,做題也沒有什麼思路,休整一下,儘快恢復狀態。
T1 a
這道題,考試的時候我一直認為要用線段樹,可是自己不會用,還不斷的hack掉自己的線段樹思路,最後沒辦法只能打了一個\(m^2*n^2\)的暴力,加上一些特殊性質,本來能拿50分,結果自己沒有考慮周到,在測試點分治的時候沒弄好,把那個二級資料直接略過了,導致只拿了30分。而正解的思路就是在暴力的基礎上優化掉一個m,達到 \(n^2*m\),具體做法就是我們只列舉左上角的頂點,因為滿足要求的右下角頂點必定在一個單調遞增的區間中,所以滿足決策單調性,可以使用二分在log的時間內算出合法的區間數,但是需要加上一些剪枝優化,具體實現見程式碼:
AC_code
#include<bits/stdc++.h>
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int N=5e4+10;
const int INF=1e7+10;
int n,m;
long long ans;
char s[35][N];
int sum[35][N];
ii read()
{
int x=0;
bool f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
signed main()
{
int l,r;
n=read();
m=read();
for(re i=1;i<=n;i++)
scanf("%s",s[i]+1);
l=read();
r=read();
for(re i=1;i<=n;i++)
{
for(re j=1;j<=m;j++)
{
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
if(s[i][j]=='1')
sum[i][j]+=1;
}
}
int L,R,nl,nr;
for(re i=1;i<=n;i++)
{
for(re k=i;k<=n;k++)
{
for(re j=1;j<=m;j++)
{
if(sum[k][m]-sum[i-1][m]-sum[k][j-1]+sum[i-1][j-1]<l || sum[k][j]-sum[i-1][j]-sum[k][j-1]+sum[i-1][j-1] >r) continue;
L=j,R=m,nl=m+1,nr=m+1;
if(sum[k][j]-sum[i-1][j]-sum[k][j-1]+sum[i-1][j-1]>=l) nl=j;
else
while(L<=R)
{
int mid=(L+R)>>1;
if(sum[k][mid]-sum[i-1][mid]-sum[k][j-1]+sum[i-1][j-1]>=l)
{
nl=mid;
R=mid-1;
}
else
L=mid+1;
}
L=j,R=m;
if(sum[k][m]-sum[i-1][m]-sum[k][j-1]+sum[i-1][j-1]<=r) nr=m;
else
while(L<=R)
{
int mid=(L+R)>>1;
if(sum[k][mid]-sum[i-1][mid]-sum[k][j-1]+sum[i-1][j-1]<=r)
{
nr=mid;
L=mid+1;
}
else
R=mid-1;
}
ans+=nr-nl+1;
}
}
}
cout<<ans<<endl;
return 0;
}
T2 b
思路:這裡記 a 的最大值為 x。
將問題轉化為“對 \(i∈[1,1e5]\),求多少種選擇方案使得 \(gcd=i\)。
繼續轉化為“對 \(i∈[1,1e5]\),求多少種選擇方案使得 \(i|gcd\),
等價於對\(i∈[1,1e5]\),求多少種選擇方案使得選的所有數均為 i 的倍數
我們預處理出\(cnt_{i,j}\)表示第 i 行中有多少個數是 j 的倍數,
那麼答案就是\(∏\limits_{i=1}^n = (cnt_{i,j} + 1) − 1\)
最後注意去重,所以我們要倒序列舉,每次去掉出現過的當前數的倍數的答案,具體實現見程式碼:
AC_code
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int mo=1e9+7;
const int N=1e5+10;
int n,m,maxx,ans;
int a[30][N];
int to[30][N],cnt[30][N];
int an[N];
ii read()
{
int x=0;
bool f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=0;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
signed main()
{
n=read();
m=read();
for(re i=1;i<=n;i++)
{
for(re j=1;j<=m;j++)
{
a[i][j]=read();
maxx=max(maxx,a[i][j]);
to[i][a[i][j]]++;
}
}
for(re i=1;i<=n;i++)
{
for(re j=1;j<=maxx;j++)
for(re k=1;k*j<=maxx;k++)
cnt[i][j]+=to[i][j*k];
}
for(re i=maxx;i;i--)
{
an[i]=1;
for(re j=1;j<=n;j++)
an[i]=(an[i]%mo*(cnt[j][i]+1))%mo;
an[i]--;
for(re j=2;j*i<=maxx;j++)
an[i]-=an[j*i];
ans=(ans%mo+an[i]%mo*i%mo+mo)%mo;
}
printf("%lld\n",(ans%mo+mo)%mo);
return 0;
}