简单组合数学+无脑前缀和优化dp计数。
f i , j f_{i,j} fi,j表示 i i i个数中逆序对数 ≤ j \le j ≤j的排列数
略卡常。。。
早年写过的代码:
#include
#define N 505
#define mod 1000000007
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
int C[N][N],f[N][N*N],T,n,k,ans,fac[N];
inline void init(){
fac[0]=1;
for(int i=1;i<=500;++i)fac[i]=1ll*fac[i-1]*i%mod;
for(int i=1;i<=500;++i)C[i][0]=C[i][i]=1,C[i][1]=i;
for(int i=2;i<=500;++i)for(int j=1;j<=i;++j){
C[i][j]=C[i-1][j]+C[i-1][j-1];
if(C[i][j]>=mod)C[i][j]-=mod;
}
int sum=0;
f[1][0]=1;
for(int i=2;i<=500;++i){
sum=0;
for(int j=0;j<=(i-1)*i/2;++j){
sum+=f[i-1][j];
if(sum>=mod)sum-=mod;
f[i][j]=sum;
if(j+1-i>=0)sum=(sum-f[i-1][j+1-i]+mod)%mod;
}
}
for(int i=2;i<=500;++i)for(int j=1;j<=(i-1)*i/2;++j){
f[i][j]+=f[i][j-1];
if(f[i][j]>=mod)f[i][j]-=mod;
}
}
int main(){
init(),T=read();
while(T--){
n=read(),k=read(),ans=0;
for(int i=1;i<=n;++i){
int tmp=1ll*C[n][i]*C[n][i]%mod*f[i][min(i*(i-1)/2,k)]%mod*fac[n-i]%mod*fac[n-i]%mod*(n-i+1)%mod;
ans+=tmp;
if(ans>=mod)ans-=mod;
}
printf("%d\n",ans);
}
return 0;
}
传送门
不知道算不算博弈论,手玩一下推 % 4 = 0 , 1 , 2 , 3 \%4=0,1,2,3 %4=0,1,2,3的堆数的奇偶性对应的答案即可。
代码:
#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
const int N=1e6+5;
int cnt[4],n;
int main(){
for(ri tt=read();tt;--tt){
n=read();
cnt[0]=cnt[1]=cnt[2]=cnt[3]=0;
for(ri i=1;i<=n;++i)cnt[read()%4]^=1;
int ans=cnt[0]*8+cnt[1]*4+cnt[2]*2+cnt[3];
if(ans==8)puts("Avnish");
else if(!ans)puts("Avnish");
else puts("Bishal");
}
return 0;
}
传送门
首先要首推一个结论:模长最长的向量长度不超过总长的一半。
然后从低维向高维推找规律即可。
然后官方题解给出了证明
代码:
#include
#define ll long long
#define mod 1000000007
using namespace std;
inline ll ksm(ll x,ll p){
ll ret=1;
while(p){
if(p&1)ret=ret*x%mod;
x=x*x%mod;
p>>=1;
}
return ret;
}
int main(){
ll n,k;
scanf("%lld%lld",&n,&k);
printf("%lld",(ksm(2,n-1)-n+mod)%mod*ksm(2,(n-1)*(mod-2))%mod);
return 0;
}
传送门
这题我用了一个挺复杂的 d p dp dp,貌似可以更简单???然而因为水过去了就懒得看其它做法了
设 f i , 0 / 1 / 2 , 0 / 1 , 0 / 1 f_{i,0/1/2,0/1,0/1} fi,0/1/2,0/1,0/1表示走到 i i i点,这一步向右走 0 / 1 / 2 0/1/2 0/1/2格( 0 0 0格表示在这里结束),第 i + 1 i+1 i+1个格子是否走过,第 i + 2 i+2 i+2个格子是否走过的档案数。
设 g i , 0 / 1 / 2 g_{i,0/1/2} gi,0/1/2表示走到 i i i点,从这一步开始一直向左走且这一步走了 0 / 1 / 2 0/1/2 0/1/2步的方案数。
然后简单讨论一下即可。
代码:
#include
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
typedef long long ll;
const int N=1e5+5,mod=1e9+7;
int n,a[N],f[N][3][2][2],g[N][3];
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void update(int&a,const int&b){a=add(a,b);}
int main(){
for(ri tt=read(),ans;tt;--tt){
n=read();
for(ri i=1;i<=n;++i){
a[i]=read();
for(ri j=0;j<3;++j)g[i][j]=0;
for(ri j=0;j<3;++j)for(ri k=0;k<2;++k)for(ri l=0;l<2;++l)f[i][j][k][l]=0;
}
for(ri i=0;i<=a[1];++i)f[1][i][1][1]=1;
ans=0;
for(ri i=1;i<=n;++i){
for(ri j=0;j<2;++j)for(ri k=0;k<2;++k)update(ans,f[i][0][j][k]);
for(ri j=0;j<3;++j)update(ans,g[i][j]);
for(ri j=0;j<2;++j)for(ri k=0;k<2;++k)if(f[i][1][j][k])for(ri l=0;l<=a[i+1];++l)update(f[i+1][l][1][j],f[i][1][j][k]);
if(a[i+1]==2)update(g[i+1][2],g[i][1]);
if(a[i]==1)continue;
for(ri j=0;j<2;++j)for(ri k=0;k<2;++k)if(f[i][2][j][k]){
update(g[i+2][1],f[i][2][j][k]);
for(ri l=0;l<=a[i+2];++l)update(f[i+2][l][0][1],f[i][2][j][k]);
if(a[i+1]==2)for(ri l=0;l<=a[i+3];++l)update(f[i+3][l][1][1],f[i][2][j][k]);
}
if(a[i+1]==2)update(g[i+2][1],g[i+1][2]);
}
cout<<ans<<'\n';
}
return 0;
}
传送门
考虑贪心,每次从后往前尽量消去连续一段是最优的,用双向链表维护一下序列即可。
代码:
#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
const int N=1e6+5;
int n,a[N],cnt[N],pre[N],nxt[N],q[N],m,ans;
inline void solve(){
int ret=q[m]-m,sum=ret;
for(ri r=m,l;r;r=l-1){
l=r;
while(l!=1&&q[l-1]==q[l]-1)--l;
sum=sum-(q[l]-q[l-1]-1)+(r-l+1);
ret=min(ret,sum);
}
ans+=ret;
}
int main(){
for(ri tt=read(),tot=0,det=0;tt;--tt,tot=0,det=0){
n=read(),ans=0;
for(ri i=1;i<=n;++i)a[i]=read();
sort(a+1,a+n+1);
for(ri i=1;i<=n;++i)if(a[i]^a[tot])a[++tot]=a[i],cnt[tot]=1;else ++cnt[tot];
n=1;
for(ri i=1;i<=tot;++i)pre[i]=i-1,nxt[i]=i+1;
while(n<=tot){
m=0;
int p=n;
while(p<=tot){
q[++m]=a[p];
--cnt[p];
if(!cnt[p])nxt[pre[p]]=nxt[p],pre[nxt[p]]=pre[p];
p=nxt[p];
}
solve();
while(n<=tot&&!cnt[n])n=nxt[n];
}
cout<<ans<<'\n';
}
return 0;
}
传送门
将 a a a数组排序,然后做一个简单的存在性 d p dp dp即可。
代码:
#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
const int N=1e5+5;
int n,d,a[N],tmp,ok[N];
inline bool check(){
for(ri i=1;i<n;++i)if(a[i+1]-a[i]>d)return 1;
return 0;
}
inline int checkl(int pos){
if(pos<2||a[pos+1]-a[1]<=d)return 1;
if(~ok[pos])return ok[pos];
for(ri i=pos-1;i;--i){
if(a[pos+1]-a[i]>d||a[pos]-a[i-1]>d)break;
if(checkl(i-1))return ok[pos]=1;
}
return ok[pos]=0;
}
inline int checkr(int pos){
if(pos>n-1||a[n]-a[pos-1]<=d)return 1;
if(~ok[pos])return ok[pos];
for(ri i=pos+1;i<=n;++i){
if(a[i]-a[pos-1]>d||a[i+1]-a[pos]>d)break;
if(checkr(i+1))return ok[pos]=1;
}
return ok[pos]=0;
}
int main(){
for(ri tt=read();tt;--tt){
n=read(),d=read(),tmp;
for(ri i=1;i<=n;++i)a[i]=read();
tmp=a[1];
sort(a+1,a+n+1);
if(check()){puts("NO");continue;}
if(a[1]==tmp||a[n]==tmp){puts("YES");continue;}
bool f=0;
memset(ok,-1,sizeof(ok));
for(ri i=1;i<=n;++i)if(a[i]==tmp){
f|=checkl(i);
break;
}
if(f)puts("YES");
else{
memset(ok,-1,sizeof(ok));
for(ri i=n;i;--i)if(a[i]==tmp){
f|=checkr(i);
break;
}
puts(f?"YES":"NO");
}
}
return 0;
}
传送门
枚举中心位置分奇偶讨论一下即可。
代码:
#include
#define ri register int
using namespace std;
typedef long long ll;
const int N=1005;
int f[N][N],sl[N][N],sr[N][N],len[N][N],n;
char s[N];
inline void init(){
for(ri i=1;i<=n;++i)f[i][i]=f[i][i-1]=1;
for(ri len=2;len<=n;++len)for(ri l=1,r=len;r<=n;++l,++r)f[l][r]=f[l+1][r-1]&(s[l]==s[r]);
for(ri l=1;l<=n;++l){
sl[l][l-1]=1;
for(ri r=l;r<=n;++r)sl[l][r]=sl[l][r-1]+f[l][r];
}
for(ri r=n;r;--r){
sr[r][r+1]=1;
for(ri l=r;l;--l)sr[r][l]=sr[r][l+1]+f[l][r];
}
}
int main(){
scanf("%s",s+1),n=strlen(s+1);
init();
ll ans=0;
for(ri i=1;i<=n;++i){
len[i][i]=0;
for(ri l=i-1,r=i+1;l>=1&&r<=n;--l,++r){
if(len[l+1][r-1])len[l][r]=len[l+1][r-1]-1;
else{
int pl=l,pr=r;
while(pl>=1&&pr<=n&&s[pl]==s[pr])++len[l][r],--pl,++pr;
}
ans+=len[l][r];
ans+=len[l][r]*(sl[l+1][r-2]-sl[l+1][l]+sr[r-1][l+2]-sr[r-1][r]);
if(l+2==r)ans+=len[l][r]*(len[l][r]+1);
}
}
for(ri i=1;i<n;++i){
len[i+1][i]=0;
for(ri l=i,r=i+1;l>=1&&r<=n;--l,++r){
if(len[l+1][r-1])len[l][r]=len[l+1][r-1]-1;
else{
int pl=l,pr=r;
while(pl>=1&&pr<=n&&s[pl]==s[pr])++len[l][r],--pl,++pr;
}
if(l+1==r)ans+=len[l][r]*len[l][r];
else{
ans+=len[l][r];
if(r-l!=1)ans+=len[l][r]*(sl[l+1][r-2]-sl[l+1][l]+sr[r-1][l+2]-sr[r-1][r]);
}
}
}
cout<<ans;
return 0;
}
传送门
大力猜结论:到一个点 p p p的答案的最大值等于 m o d p mod_p modp减去从根到叶子路径上所有点权的 g c d gcd gcd与 m o d p mod_p modp的 g c d gcd gcd。
证明不会然而貌似水过了这道题。
#include
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
typedef long long ll;
typedef pair<int,ll> pli;
const int N=1e5+5;
vector<int>e[N];
vector<pli>ans;
int n;
ll a[N],mod[N];
inline ll readl(){
ll ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
inline void dfs(int p,int fa,ll pre){
int siz=0;
pre=__gcd(pre,a[p]);
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i])==fa)continue;
dfs(v,p,pre),++siz;
}
if(!siz){
ll tmp=__gcd(mod[p],pre);
ans.push_back(pli(p,(mod[p]/tmp-1)*tmp));
}
}
int main(){
for(ri tt=read();tt;--tt){
n=read();
for(ri i=1;i<=n;++i)e[i].clear();
for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
for(ri i=1;i<=n;++i)a[i]=readl();
for(ri i=1;i<=n;++i)mod[i]=readl();
ans.clear();
dfs(1,0,0);
sort(ans.begin(),ans.end());
for(ri i=0;i<ans.size();++i)cout<<ans[i].se<<' ';
puts("");
}
return 0;
}
传送门
考虑答案如何凑出。
假设给出的链是 ( u , v ) (u,v) (u,v),那么如果另外一条链跟它只有一个交点 p p p说明那条链剩余部分在 p p p的子树中(如果交点是链顶则还要考虑 p p p子树外连通块的贡献),然后就可以预处理出 f p f_p fp表示以 p p p为 l c a lca lca的路径有几条,然后统计链上面的 f f f值和,再扣掉不合法部分。(详见代码)
代码:
#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
const int N=250005;
typedef long long ll;
ll f[N],g[N],h[N];
int n,m,siz[N],hson[N],dep[N],top[N],fa[N];
vector<int>e[N];
void dfs1(int p){
f[p]=siz[p]=1,hson[p]=0;
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i])==fa[p])continue;
fa[v]=p,dep[v]=dep[p]+1,dfs1(v),f[p]+=(ll)siz[p]*siz[v],siz[p]+=siz[v];
if(siz[v]>siz[hson[p]])hson[p]=v;
}
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i])==fa[p])continue;
g[v]=(ll)(siz[p]-siz[v])*siz[v];
}
}
void dfs2(int p,int tp){
top[p]=tp,h[p]+=f[p]-g[p];
if(!hson[p])return;
h[hson[p]]=h[p],dfs2(hson[p],tp);
for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])!=hson[p]&&v!=fa[p])h[v]=h[p],dfs2(v,v);
}
inline int lca(int x,int y){
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
inline int lcca(int x,int t){
while(top[x]^top[t])if(fa[top[x]]==t)return top[x];else x=fa[top[x]];
return hson[t];
}
int main(){
for(ri u,v,tt=read();tt;--tt){
n=read(),m=read();
for(ri i=1;i<=n;++i)e[i].clear();
for(ri i=1;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
h[1]=0,dfs1(1),dfs2(1,1);
while(m--){
u=read(),v=read();
if(u==v){
ll ans=f[u]+(ll)siz[u]*(n-siz[u]);
cout<<ans<<'\n';
continue;
}
int t=lca(u,v),s,a,b;
if(t==u)swap(u,v);
if(t==v){
ll ans=h[u]-h[t];
s=lcca(u,t);
a=siz[t]-siz[s];
ans+=f[t]+(ll)a*(n-siz[t]);
cout<<ans<<'\n';
continue;
}
ll ans=h[u]+h[v]-2*h[t];
s=lcca(u,t),a=lcca(v,t);
ans+=(ll)siz[s]*siz[a];
b=siz[t]-siz[s]-siz[a];
ans+=f[t]+(ll)b*(n-siz[t]);
cout<<ans<<'\n';
}
}
return 0;
}
传送门
这题被我暴力 C a O CaO CaO过去了。
我是枚举了所有 a 1 a_1 a1的因数作为其中一个数列的 g c d gcd gcd。
然而有一个结论:假设除了最大值和次大值的其它数 g c d = x gcd=x gcd=x,那么 a n s = max { g c d ( x , a m a x ) + a s e c o n d _ m a x , g c d ( x , a s e c o n d _ m a x + a m a x ) } ans=\max\{gcd(x,a_{max})+a_{second\_max},gcd(x,a_{second\_max}+a_{max})\} ans=max{gcd(x,amax)+asecond_max,gcd(x,asecond_max+amax)}
于是可以 O ( n l o g n ) O(nlogn) O(nlogn)搞完(我tcl只会暴力而且这题数据贼水)
暴力代码:
#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
typedef long long ll;
const int N=10005;
int n,k;
ll s[N],x,a[N];
int main(){
for(ri tt=read();tt;--tt){
n=read();
ll ans=0,det=0;
for(ri i=1;i<=n;++i)a[i]=read(),ans+=a[i];
k=read(),x=read();
if(!x){
cout<<ans<<'\n';
continue;
}
for(ri i=1;i<=n;++i)a[i]=(a[i]^x)-a[i];
sort(a+1,a+n+1),reverse(a+1,a+n+1);
for(ri i=1;i<=n;++i)s[i]=s[i-1]+a[i];
if(k==n){
cout<<ans+max(0ll,s[n])<<'\n';
continue;
}
for(ri i=2;i<=n;i+=2)det=max(det,s[i]);
if(k&1)for(ri i=1;i<=n;++i)det=max(det,s[i]);
cout<<det+ans<<'\n';
}
return 0;
}
传送门
简单推一推发现空段满足等差数列求和公式。
然后就完了 Q w Q QwQ QwQ。
代码:
#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
typedef long long ll;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline ll read(){
ll ans=0;
char ch=gc();
bool f=1;
while(!isdigit(ch))f^=ch=='-',ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return f?ans:-ans;
}
const int N=1e5+5;
const ll mod=1e9+7;
ll k,n;
int main(){
ll inv=(mod+1)/2;
for(ri tt=read();tt;--tt){
n=read(),k=read();
if(n>=k){
cout<<(int)((k-1)%mod)<<'\n';
continue;
}
ll t=k;
t-=n;
t=t%(n-1)?t/(n-1)+1:t/(n-1);
++t;
ll a=k-1,b=a-(t-1)%mod*(n-1)%mod;
a%=mod,b=(b%mod+mod)%mod;
cout<<(int)((a+b)%mod*inv%mod*(t%mod)%mod)<<'\n';
}
return 0;
}