4月做题记录
省选惨败,决定发奋。
发现1只能连自己,若非,则\(a[1]\rightarrow 1\)的链上均不合法。
题意转化为改变最少的连边使树的深度不超过\(m\),简单\(dp\)即可。
#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
int n,m,a[maxn],cnt,f[maxn];
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 beg[maxn],nex[maxn],to[maxn],e;
inline void add(int x,int y){
e++;nex[e]=beg[x];
beg[x]=e;to[e]=y;
}
inline void dfs(int x,int fa){
for(int i=beg[x];i;i=nex[i]){
int t=to[i];
if(t==fa)continue;
dfs(t,x);
f[x]=max(f[x],f[t]+1);
}
if(f[x]==m-1&&x!=1&&a[x]!=1){
cnt++;f[x]=-1;
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++)
a[i]=read();
if(a[1]>1)cnt=1;
for(int i=2;i<=n;i++)
add(a[i],i);
dfs(1,0);
printf("%d\n",cnt);
return 0;
}
AT2163 [AGC006B] Median Pyramid Easy
发现性质:若有两个连续的相同的数,则这两个数可以一直保持。
故将顶点的数放在最中间,构造下一行有两个这样的数在中间且在一起就好了。
#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
int n,tp,a[maxn],vis[maxn],pos=1;
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 main(){
n=read(),tp=read();
if(tp==1||tp==2*n-1)return puts("No"),0;
puts("Yes");
if(n==2)return printf("1\n2\n3\n"),0;
a[n]=tp;vis[tp]=1;
if(tp==2){
a[n-1]=1;a[n-2]=4;a[n+1]=3;
vis[1]=vis[3]=vis[4]=1;
}else if(tp==2*n-2){
a[n-1]=2*n-1;a[n-2]=2*n-4;a[n+1]=2*n-3;
vis[2*n-1]=vis[2*n-3]=vis[2*n-4]=1;
}else{
a[n-1]=tp-1;a[n-2]=tp+2;a[n+1]=tp+1;
vis[tp-1]=vis[tp+1]=vis[tp+2]=1;
}
for(int i=1;i<=2*n-1;i++){
if(a[i])continue;
while(vis[pos])pos++;
a[i]=pos;vis[pos]=1;
}
for(int i=1;i<=2*n-1;i++)
printf("%d\n",a[i]);
return 0;
}
AT2164 [AGC006C] Rabbit Exercise
考虑\(a_i\)即为期望,则转移显然\(a_i=a_{i-1}+a_{i+1}-a_i\)
由于\(k\le 10^{18}\),考虑倍增。而\(m\le 10^5\),矩乘不现实。
考虑差分,\(d_i=a_i-a_{i-1}\),修改后\(d_i=a_{i-1}+a_{i+1}-a_i-a_{i-1}=a_{i+1}-a_i=d_{i+1}\quad d_{i+1}=a_{i+1}-a_{i-1}-a_{i+1}+a_i=a_i-a_{i-1}=d_i\)
则一次操作相当于交换\(d_i\)和\(d_{i+1}\),倍增即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+10;
const int mod=1e9+7;
int n,m,k,a[maxn],b[maxn],p[maxn],d[maxn],tmp[maxn];
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;
}
signed main(){
n=read();
for(int i=1;i<=n;i++)
a[i]=read(),p[i]=b[i]=i;
m=read(),k=read();
for(int i=1,x;i<=m;i++)
x=read(),swap(p[x],p[x+1]);
while(k){
if(k&1){
for(int i=1;i<=n;i++)tmp[i]=b[p[i]];
for(int i=1;i<=n;i++)b[i]=tmp[i];
}
for(int i=1;i<=n;i++)tmp[i]=p[p[i]];
for(int i=1;i<=n;i++)p[i]=tmp[i];
k>>=1;
}
for(int i=1;i<=n;i++)
d[i]=a[b[i]]-a[b[i]-1]+d[i-1];
for(int i=1;i<=n;i++)
printf("%lld\n",d[i]);
return 0;
}
真搞不懂这个差分是怎么想到的。
CF585E Present for Vitalik the Philatelist
学会新科技:狄利克雷前缀和、后缀和以及逆操作。
易知,只要满足\(\gcd(x,\gcd{S})=1\)且\(\gcd{S}>1\)则有\(x\notin {S}\)。考虑设\(f_i\)代表集合中与\(i\)互质的数的个数,\(g_i\)为\(\gcd\)恰好为\(i\)的子集个数,则答案为\(\sum f_i g_i\)。
考虑求\(f_n=\sum\limits_{i=1}^n [\gcd(i,n)=1] c_i=\sum\limits_{i=1}^n c_i \sum\limits_{d|{\gcd(i,n)}}\mu(i)=\sum\limits_{i|n}\mu(i)\sum\limits_{i|d}c_d\),\(\text{dirichlet}\)前后缀合处理即可。
考虑求\(g_i\),恰好不好求,则转求\(\{p_i\}\)为\(\gcd\)为\(i\)的倍数的子集个数,由于每个数必为\(i\)的倍数,则\(p_i=2^{\sum\limits_{i|d}c_d}-1\)
由于\(p_i=\sum\limits_{d|i} g_i\),故对其做一次逆操作即可。时间复杂度\(\Theta(N\log\log N)\),其中\(N\)为\(a_i\)最大值。
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
const int N=1e7+10;
const int mod=1e9+7;
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 pri[N/10],tot,mu[N];bool flag[N];
int n,lim,c[N],g[N],s[N],p[maxn],ans;
int main(){
n=read();
for(int i=1,x;i<=n;i++)
x=read(),lim=max(lim,x),c[x]++;
mu[1]=p[0]=1;
for(int i=2;i<=lim;i++){
if(!flag[i])pri[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&i*pri[j]<=lim;j++){
flag[i*pri[j]]=1;
if(i%pri[j]==0){mu[i*pri[j]]=0;break;}
mu[i*pri[j]]=-mu[i];
}
}
for(int i=1;i<=tot;i++)
for(int j=lim/pri[i];j;j--)
c[j]+=c[j*pri[i]];
for(int i=1;i<=lim;i++)
g[i]=c[i]*mu[i];
for(int i=1;i<=tot;i++)
for(int j=1;j*pri[i]<=lim;j++)
g[j*pri[i]]+=g[j];
for(int i=1;i<=n;i++)
p[i]=2ll*p[i-1]%mod;
for(int i=1;i<=lim;i++)
s[i]=p[c[i]]-1;
for(int i=tot;i;i--)
for(int j=1;j*pri[i]<=lim;j++)
s[j]=(s[j]-s[j*pri[i]]+mod)%mod;
for(int i=2;i<=lim;i++)
ans=(ans+1ll*g[i]*s[i]%mod)%mod;
printf("%d\n",ans);
return 0;
}
直接暴力反演+后缀和+卷积即可。注意\(d * \mu = 1\)
#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e6+10;
int n,m,mod,ans;
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 pri[maxn],tot,mu[maxn],flag[maxn];
int val[maxn],a[maxn],b[maxn],d[maxn];
int main(){
n=read(),m=read(),mod=read();
if(n>m)swap(n,m);
mu[1]=1;
for(int i=2;i<=m;i++){
if(!flag[i])pri[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&i*pri[j]<=m;j++){
flag[i*pri[j]]=1;
if(i%pri[j]==0){mu[i*pri[j]]=0;break;}
mu[i*pri[j]]=-mu[i];
}
}
for(int i=1;i<=m;i++)
for(int j=i;j<=m;j+=i)val[j]++;
for(int i=1;i<=n;i++)a[i]=val[i];
for(int i=1;i<=tot;i++)
for(int j=n/pri[i];j;j--)
a[j]=(a[j]+a[j*pri[i]])%mod;
for(int i=1;i<=m;i++)b[i]=val[i];
for(int i=1;i<=tot;i++)
for(int j=m/pri[i];j;j--)
b[j]=(b[j]+b[j*pri[i]])%mod;
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j+=i)
d[j]=(d[j]+val[i]*mu[j/i])%mod;
for(int i=1;i<=n;i++)
ans=(ans+1ll*a[i]*b[i]%mod*d[i]%mod)%mod;
printf("%d\n",(ans+mod)%mod);
return 0;
}
由\(\text{bezout}\)定理,显然集合\(\{S\}\)满足条件当且仅当\(\gcd(\{S\})=1\),令\(T=\{1,2,...,n\}\)
\(\sum\limits_{S\in T} [\gcd(\{S\})=1]=\sum\limits_{S\in T} \sum\limits_{d|\gcd(\{S\})}\mu(d)=\sum\limits_{d=1}^n \mu(d) \sum\limits_{S \in T , d|\gcd(\{S\}} 1=\sum\limits_{d=1}^n \mu(d)(2^{[\frac{n}{d}]}-1)\)
杜教筛即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e7;
const int mod=1e9+7;
int n,pri[N+5],tot,flag[N+5],ans;
int mu[N+5];map<int,int>f;
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;
}
inline int ksm(int x,int y){
int res=1;
while(y){
if(y&1)res=res*x%mod;
x=x*x%mod;y>>=1;
}
return res;
}
inline int sum(int n){
if(n<=N)return mu[n];
if(f[n])return f[n];
int ans=1;
for(int l=2,r;l<=n;l=r+1){
r=n/(n/l);
ans=(ans-(r-l+1)*sum(n/l)%mod+mod)%mod;
}
return f[n]=ans;
}
signed main(){
mu[1]=1;
for(int i=2;i<=N;i++){
if(!flag[i])pri[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&i*pri[j]<=N;j++){
flag[i*pri[j]]=1;
if(i%pri[j]==0){mu[i*pri[j]]=0;break;}
mu[i*pri[j]]=-mu[i];
}
}
for(int i=1;i<=N;i++)mu[i]+=mu[i-1];
n=read();
for(int l=1,r;l<=n;l=r+1){
r=n/(n/l);
ans=(ans+(ksm(2,n/l)-1)*(sum(r)-sum(l-1))%mod)%mod;
}
printf("%lld\n",(ans+mod)%mod);
return 0;
}
想法和P4948 数列求和一样,列式子裂项递推即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=5e3+10;
const int mod=998244353;
int n,m,k,ans,a,b;
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 c[maxn][maxn],f[maxn];
inline int ksm(int x,int y){
int res=1;
while(y){
if(y&1)res=res*x%mod;
x=x*x%mod;y>>=1;
}
return res;
}
signed main(){
n=read(),m=read(),k=read();
if(m==1)return printf("%lld\n",n),0;
a=ksm(m-1,mod-2),b=ksm(a+1,mod-2);
c[0][0]=1;f[0]=ksm(a+1,n);
for(int i=1;i<=k;i++){
c[i][0]=1;
for(int j=1;j<=i;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
for(int i=1;i<=k;i++){
int t1=0,t2=0;
for(int j=0;j<=i-1;j++)
t1=(t1+c[i-1][j]*f[j]%mod)%mod;
for(int j=0;j<=i-2;j++)
t2=(t2+c[i-1][j]*f[j+1]%mod)%mod;
f[i]=a*b%mod*(n*t1%mod-t2+mod)%mod;
}
printf("%lld\n",f[k]*ksm(m-1,n)%mod*ksm(ksm(m,mod-2),n)%mod);
return 0;
}
由上一题改几个字就过了,不放代码。
套路反悔贪心,按截止时间排序,不断放小的进去。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+10;
const int mod=1e9+7;
int n,ans,tot;
struct node{int a,b;}p[maxn];
inline int cmp(node x,node y){return x.b<y.b;}
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;
}
priority_queue<int>q;
signed main(){
n=read();
for(int i=1;i<=n;i++)
p[i].a=read(),p[i].b=read();
sort(p+1,p+1+n,cmp);
for(int i=1;i<=n;i++)
if(tot+p[i].a<=p[i].b){
tot+=p[i].a;ans++;
q.push(p[i].a);
}else if(p[i].a<q.top()){
tot-=q.top();q.pop();
q.push(p[i].a);tot+=p[i].a;
}
printf("%lld\n",ans);
return 0;
}
CF730I Olympiad in Programming and Sports
套路费用流,难怪评分低……
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf 0x7f
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;
}
const int maxn=1e5+100;
int beg[maxn],nex[maxn],to[maxn],f[maxn],c[maxn],e,mf,mc;
void add(int x,int y,int z,int d){
nex[e]=beg[x];beg[x]=e;
to[e]=y;f[e]=z;c[e]=d;e++;
}
int n,m,st,ed,dis[maxn],vis[maxn],pre[maxn],val[maxn],flow[maxn];
queue<int>q;
int spfa(){
memset(dis,inf,sizeof(dis));
memset(flow,inf,sizeof(flow));
memset(vis,0,sizeof(vis));
dis[st]=0;pre[ed]=-1;vis[st]=1;
q.push(st);
while(!q.empty()){
int x=q.front();
q.pop();
vis[x]=0;
for(int i=beg[x];~i;i=nex[i]){
int t=to[i];
if(f[i]&&dis[t]>dis[x]+c[i]){
dis[t]=dis[x]+c[i];
pre[t]=x;val[t]=i;
flow[t]=min(flow[x],f[i]);
if(!vis[t]){
vis[t]=1;
q.push(t);
}
}
}
}
return pre[ed]!=-1;
}
int a[maxn],b[maxn],cnt,f1,f2,p1,p2;
inline void Add(int x,int y,int z,int d){add(x,y,z,d),add(y,x,0,-d);}
signed main(){
memset(beg,-1,sizeof(beg));
n=read(),f1=read(),f2=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)b[i]=read();
cnt=n;st=++cnt;ed=++cnt;p1=++cnt;p2=++cnt;
Add(st,p1,f1,0);Add(st,p2,f2,0);
for(int i=1;i<=n;i++){
Add(p1,i,1,-a[i]);Add(p2,i,1,-b[i]);
Add(i,ed,1,0);
}
while(spfa()){
int now=ed;
mf+=flow[ed];
mc+=flow[ed]*dis[ed];
while(now!=st){
f[val[now]]-=flow[ed];
f[val[now]^1]+=flow[ed];
now=pre[now];
}
}
printf("%lld\n",-mc);
for(int i=1;i<=n;i++)
for(int j=beg[i];~j;j=nex[j])
if(f[j]&&to[j]==p1)printf("%lld ",i);
puts("");
for(int i=1;i<=n;i++)
for(int j=beg[i];~j;j=nex[j])
if(f[j]&&to[j]==p2)printf("%lld ",i);
puts("");
return 0;
}
一开始把题看错了,折腾了好一会
\(\sum\limits_{S} |S|\gcd S=\sum\limits_{d>1} |S|d[\gcd S=d]=\sum\limits_{d>1} d \sum\limits_{S} |S| \sum\limits_{di|\gcd S} \mu(i)=\sum\limits_{S} |S|\sum\limits_{t|\gcd}\sum\limits_{d|t,d>1}d\mu(\frac{t}{d})=\sum\limits_{t}(\sum\limits_{d|t,d>1}d\mu(\frac{t}{d}))(\sum\limits_{t|\gcd S}|S|)\)
#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
const int inv2=(mod+1)/2;
const int N=1e6;
int n,ans,p[maxn],pri[maxn],tot,flag[N+5];
int mu[N+5],b[N+5],a[N+5];
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 main(){
n=read();
for(int i=1;i<=n;i++)a[read()]++;
mu[1]=p[0]=1;
for(int i=1;i<=n;i++)p[i]=2ll*p[i-1]%mod;
for(int i=2;i<=N;i++){
if(!flag[i])pri[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&i*pri[j]<=N;j++){
flag[i*pri[j]]=1;
if(i%pri[j]==0){
mu[i*pri[j]]=0;
break;
}
mu[i*pri[j]]=-mu[i];
}
}
for(int i=2;i<=N;i++)
for(int j=i;j<=N;j+=i)
b[j]=(b[j]+mu[j/i]*i)%mod;
for(int i=1;i<=tot;i++)
for(int j=N/pri[i];j;j--)
a[j]=(a[j]+a[pri[i]*j])%mod;
for(int i=2;i<=N;i++)
if(a[i])ans=(ans+1ll*p[a[i]-1]*a[i]%mod*b[i]%mod)%mod;
printf("%d\n",(ans+mod)%mod);
return 0;
}
CF301E Yaroslav and Arrangements
\(dp\)思路很显然,但我就是想不到。写了一片题解。
#include <bits/stdc++.h>
#define ll long long
#define mod 1000000007
#define N 109
using namespace std;
int c[N][N],n,m,K,dp[2][N][N][N],ans;
void add(int &x,int y){x=(x+y>=mod)?x+y-mod:x+y;}
int main(){
cin>>n>>m>>K;n++;
c[0][0]=1;
for(int i=1;i<=K;i++)
for(int j=0;j<=i;j++){
c[i][j]=(j?c[i-1][j-1]:0)+c[i-1][j];
if(c[i][j]>K)c[i][j]=K+1;
}
int now=1,las=0;
dp[now][0][1][1]=1;
for(int i=1;i<=m;i++){
las=now,now^=1;
memset(dp[now],0,sizeof(dp[now]));
for(int j=0;j<=n;j++)
for(int k=1;k<=n;k++)
for(int l=1;l<=K;l++)
if(dp[las][j][k][l])
for(int t=k;t<=n-j;t++)
if(l*c[t-1][k-1]<=K)
add(dp[now][j+t][t-k][l*c[t-1][k-1]],dp[las][j][k][l]);
int tmp=0;
for(int j=2;j<=n;j++)
for(int l=1;l<=K;l++)add(tmp,dp[now][j][0][l]);
add(ans,(ll)tmp*(m-i+1)%mod);
}
cout<<ans<<'\n';
return 0;
}
第一眼:\(n\)个数,按顺序取,分\(a\)段,每段中取最多权值和不超过\(b\)的个数和的最大值。只会\(n^2\log n\),评分2100?爬爬爬
第二眼:听说每行内的数要连续?还是不会,我菜了啊
第三眼:全文连续……这不是有什么好做的吗?预处理+倍增,\(\Theta(n\log n)\)
#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=1e6+10;
const int mod=1e9+7;
int n,a,b,p[maxn],sum[maxn],nex[maxn][21],ans,l;
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;
}
string s[maxn];
int main(){
n=read(),a=read(),b=read()+1;
for(int i=1;i<=n;i++)
cin>>s[i],p[i]=s[i].size()+1;
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+p[i];
for(int i=1;i<=n;i++)
nex[i][0]=upper_bound(sum+1,sum+n+1,sum[i-1]+b)-sum;
nex[n+1][0]=n+1;
for(int i=1;i<=20;i++)
for(int j=1;j<=n+1;j++)
nex[j][i]=nex[nex[j][i-1]][i-1];
for(int i=1;i<=n;i++){
int r=i;
for(int j=0;j<=20;j++)
if(a>>j&1)r=nex[r][j];
if(ans<r-i)ans=r-i,l=i;
}
while(ans){
for(int i=l;i<nex[l][0];i++)
cout<<s[i]<<(i==nex[l][0]-1?'\n':' ');
ans-=nex[l][0]-l;l=nex[l][0];
}
return 0;
}