NOIP提高组模拟赛19
a
\(N\)很小\(M\)很大,猜测需要\(O(N^2M)\)的算法,就是枚举\(N\)得到一段序列然后\(O(M)\)求贡献
随着左界的右移,满足条件的区间一定不会左移,使用双指针(或许是三个)
code
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=50005;
int mp[35][maxn],n,m;
char c[maxn];
int sum(int i,int j,int l,int r){
return mp[j][r]-mp[i-1][r]-mp[j][l-1]+mp[i-1][l-1];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%s",c+1);
for(int j=1;j<=m;++j)
mp[i][j]=c[j]-'0'+mp[i-1][j]+mp[i][j-1]-mp[i-1][j-1];
}
int l,r;scanf("%d%d",&l,&r);
ll ans=0;
for(int i=1;i<=n;++i)
for(int j=i;j<=n;++j){
int p1=1,p2=1;
for(int k=1;k<=m;++k){
while(p1<=m&&sum(i,j,k,p1)<l)++p1;
while(p2<=m&&sum(i,j,k,p2)<=r)++p2;
if(p1<k)p1=k;
ans=ans+p2-p1;
}
}
printf("%lld\n",ans);
return 0;
}
b
发现值域不大,可以计算\(LCA=i\)的方案数,最后统计答案,复杂度\(O(NMa_{max})\)
考虑优化
虽然不能很快求出\(LCA=i\)的方案数,但是可以较快求出\(i|GCD\)的方案数
然后这个东西可以简单容斥掉,使用莫比乌斯反演可以更快(我不会)
如何求出\(i|GCD\)的方案数,选的每个数都是\(i\)的倍数,那么
\(𝑂(𝑛(𝑚+𝑥ln𝑥)\))处理出第\(i\)行有多少个数是\(j\)的倍数,记为\(𝑐𝑛𝑡_{i,j}\)
共有\(∏ _{i=1}^n(𝑐𝑛𝑡_{𝑖,𝑗} +1)-1\)种方案,使得选的所有数均为\(j\)的倍数,复杂度\(O(NM)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+5;
int mp[23][maxn],n,m,x;
int cnt[23][maxn];
int ans[maxn];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
int y;scanf("%d",&y);
++mp[i][y];
x=max(x,y);
}
for(int i=1;i<=n;++i){
for(int k=1;k<=x;++k)
for(int p=k;p<=x;p+=k)
cnt[i][k]+=mp[i][p];
}
for(int i=1;i<=x;++i){
ans[i]=1;
for(int j=1;j<=n;++j)
ans[i]=1ll*ans[i]*(cnt[j][i]+1)%mod;
ans[i]=(1ll*ans[i]-1+mod)%mod;
}
int pr=0;
for(int i=x;i;--i){
for(int j=i+i;j<=x;j+=i)
ans[i]=(1ll*ans[i]-ans[j]+mod)%mod;
pr=(1ll*ans[i]*i%mod+pr)%mod;
}
printf("%d\n",pr);
return 0;
}
c
如果忽略重边,那么原图就是一棵树
发现我们要尽量选择颜色不同的边来走,那么对于颜色很多的边,对颜色去重,保留任意三种不同颜色一定可以得到答案
然后需要一个\(DP\)解决问题,由于蒟蒻不会点分治,使用倍增实现
设\(f[i][j][l][r]\)表示从\(i\)号点到向上\(2^j\)级祖先,第一条边颜色为\(i\),最后一条边颜色为\(j\)的答案,然后倍增即可
使用结构体可以更方便的进行操作
code
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e5;
int n,m,head[maxn],tot,fa[maxn][19],dep[maxn];
struct DP{
int c1[3],c2[3],p1,p2,a[3][3];
DP(){memset(a,0,sizeof(a));p1=-1;p2=-1;}
void pre(int col){
c1[++p1]=col;
c2[++p2]=col;
a[p1][p2]=1;
}
friend DP operator + (const DP &x,const DP &y){
DP ans;ans.p1=x.p1;ans.p2=y.p2;
for(int i=0;i<=ans.p1;++i)ans.c1[i]=x.c1[i];
for(int i=0;i<=ans.p2;++i)ans.c2[i]=y.c2[i];
for(int l=0;l<=ans.p1;++l){
for(int r=0;r<=ans.p2;++r){
for(int i=0;i<=x.p2;++i){
for(int j=0;j<=y.p1;++j){
int ls=x.a[l][i]+y.a[j][r];
if(x.c2[i]==y.c1[j])--ls;
ans.a[l][r]=max(ans.a[l][r],ls);
}
}
}
}
return ans;
}
void res(){
DP ls;ls.p1=p2;ls.p2=p1;
for(int i=0;i<=ls.p1;++i)
for(int j=0;j<=ls.p2;++j)
ls.a[i][j]=a[j][i];
for(int i=0;i<=ls.p1;++i)ls.c1[i]=c2[i];
for(int i=0;i<=ls.p2;++i)ls.c2[i]=c1[i];
(*this)=ls;
}
void get_ans(){
int ans=0;
for(int i=0;i<=p1;++i)
for(int j=0;j<=p2;++j)
ans=max(ans,a[i][j]);
printf("%d\n",ans);
}
}dp[maxn][19];
struct edge{int net,to,col;}e[maxn*6+5];
struct ll{int u,v,w;}ee[maxn*6+5];
bool cmp(ll x,ll y){return x.u==y.u?x.v<y.v:x.u<y.u;}
void add(int u,int v,int w){
e[++tot].net=head[u];
head[u]=tot;
e[tot].to=v;
e[tot].col=w;
}
void DFS(int x){
for(int i=head[x];i;i=e[i].net){
int v=e[i].to;
if(v==fa[x][0])continue;
dp[v][0].pre(e[i].col);
if(fa[v][0])continue;
dep[v]=dep[x]+1;
fa[v][0]=x;
DFS(v);
}
}
void work(int u,int v){
if(u==v){
printf("0\n");
return;
}
if(dep[v]>dep[u])swap(u,v);
DP lu,lv;bool f1=1,f2=1;
for(int i=18;i>=0;--i)
if(dep[u]-dep[v]>=(1<<i)){
if(f1)lu=dp[u][i],f1=0;
else lu=lu+dp[u][i];
u=fa[u][i];
}
if(u==v){
lu.get_ans();
return;
}
for(int i=18;i>=0;--i)
if(fa[u][i]!=fa[v][i]){
if(f1)lu=dp[u][i],f1=0;
else lu=lu+dp[u][i];
if(f2)lv=dp[v][i],f2=0;
else lv=lv+dp[v][i];
u=fa[u][i];
v=fa[v][i];
}
if(f1)lu=dp[u][0];
else lu=lu+dp[u][0];
lu=lu+dp[v][0];
if(!f2){
lv.res();
lu=lu+lv;
}
lu.get_ans();
return;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
scanf("%d%d%d",&ee[i].u,&ee[i].v,&ee[i].w);
if(ee[i].u>ee[i].v)swap(ee[i].u,ee[i].v);
}
sort(ee+1,ee+m+1,cmp);
int lu=-1,lv,num,c1,c2;
for(int i=1;i<=m;++i){
if(ee[i].u==lu&&ee[i].v==lv){
if(num>=3)continue;
if(c1==ee[i].w)continue;
if(num==2&&c2==ee[i].w)continue;
++num;c2=ee[i].w;
}else{
num=1;lu=ee[i].u;lv=ee[i].v;c1=ee[i].w;
}
add(ee[i].u,ee[i].v,ee[i].w);
add(ee[i].v,ee[i].u,ee[i].w);
}
DFS(1);
for(int j=1;j<=18;++j)
for(int i=1;i<=n;++i){
fa[i][j]=fa[fa[i][j-1]][j-1];
dp[i][j]=dp[i][j-1]+dp[fa[i][j-1]][j-1];
}
int Q;scanf("%d",&Q);
for(int i=1;i<=Q;++i)
{
int x,y;
scanf("%d%d",&x,&y);
work(x,y);
}
return 0;
}