A
贪心一段一段选,每次越大越好。
证明?
设f[i]表示前i个至少多少下。
f显然有单调性。
那么对于一个i找到一个最大的j,然后f[i]=f[i-j]+1
这样看看就是贪心啊。
#include<cstdio>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10;
map<int,bool> b;
int f[maxn],left[maxn],a[maxn];
int i,j,k,l,t,n,m,ans,ca;
int main(){
scanf("%d",&ca);
while (ca--){
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i]);
b.clear();
i=n;
ans=0;
while (i){
j=i;
b[a[i]]=1;
while (j>1&&!b[a[j-1]]){
b[a[j-1]]=1;
j--;
}
fo(k,j,i) b[a[k]]=0;
ans++;
i=j-1;
}
printf("%d\n",ans);
}
}
B
枚举开头在哪里,这样前导0比较容易判。
考虑判定cnt0个0,cnt1个1,cnt2个2,能否恰好取k个和模3为v。
0没有影响,可以考虑最大化cnt1和cnt2的选取个数,使和模3为0,然后看看剩余需要的是否<=cnt0。
v=1的话对于取1个1和2个2都试试
v=2的话对于取2个1和1个2都试试
于是问题变成使和模3为0
取1个1和1个2不变
3个1或3个2也不变
前者假如做了k次,我们可以看做做了k%3次,接下来都用后者表示。
那么对于前者我们可以只做0~2次。
剩下都是后者,能取尽量取。
然后这题结束了吗?
注意一个0不算前导0,特判掉即可。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=100000+10;
int a[maxn];
int i,j,k,l,t,v,n,m,ca,cnt0,cnt1,cnt2;
bool czy;
int read(){
char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
return ch-'0';
}
int work(int x,int y,int k){
if (!k) return 0;
int i,l=0,kk,xx,yy,ans=k;
fo(i,0,2){
kk=k;xx=x;yy=y;
xx-=i;yy-=i;
if (xx<0||yy<0) break;
kk-=2*i;
if (kk<0) break;
t=min(kk/3,xx/3);
kk-=3*t;
t=min(kk/3,yy/3);
kk-=3*t;
ans=min(ans,kk);
}
return ans;
}
int main(){
scanf("%d",&ca);
while (ca--){
scanf("%d%d",&n,&m);
cnt0=cnt1=cnt2=v=0;
fo(i,1,n){
a[i]=read();
(v+=a[i])%=3;
if (a[i]%3==0) cnt0++;
else if (a[i]%3==1) cnt1++;
else cnt2++;
}
czy=0;
fo(i,1,m+1){
if (i>1) (v-=a[i-1])%=3;
(v+=3)%=3;
if (a[i]%3==0) cnt0--;
else if (a[i]%3==1) cnt1--;
else cnt2--;
if (a[i]==0) continue;
if (v==0){
if (work(cnt1,cnt2,m-i+1)<=cnt0){
czy=1;
break;
}
}
else if (v==1){
if (cnt1&&m-i>=0&&work(cnt1-1,cnt2,m-i)<=cnt0){
czy=1;
break;
}
if (cnt2>=2&&m-i-1>=0&&work(cnt1,cnt2-2,m-i-1)<=cnt0){
czy=1;
break;
}
}
else if (v==2){
if (cnt1>=2&&m-i-1>=0&&work(cnt1-2,cnt2,m-i-1)<=cnt0){
czy=1;
break;
}
if (cnt2&&m-i>=0&&work(cnt1,cnt2-1,m-i)<=cnt0){
czy=1;
break;
}
}
}
if (m==n-1){
fo(i,1,n)
if (!a[i]){
czy=1;
break;
}
}
if (czy) printf("yes\n");else printf("no\n");
}
}
C
设一下f[i]表示长度为n的序列恰好只出现i种数有多少这样的序列。
考虑容斥有
f[i]=in−∑i−1j=1f[j]∗Cji
对于长度相同的环缩成一起。
一共最多只有6种不同长度的环。
枚举每种环取多少个。
假如一个有k个节点的环取了j个,乘个组合数。
把所有取了至少一个的环求lcm。
最后如果一共取了z个点还要乘f[z]
就是这样。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int mo=1000000007;
int a[7],c[7],b[30],bz[30],f[30];
int C[30][30];
bool pd[30];
int i,j,k,l,t,n,m,top,ca,ans;
char ch;
char get(){
char ch=getchar();
while (ch<'a'||ch>'z') ch=getchar();
return ch;
}
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
int lcm(int a,int b){
return a*b/gcd(a,b) ;
}
int quicksortmi(int x,int y){
if (!y) return 1;
int t=quicksortmi(x,y/2);
t=(ll)t*t%mo;
if (y%2) t=(ll)t*x%mo;
return t;
}
/*void dfs(int x,int y,int z,int f){
if (x==top+1){
(ans+=(ll)f*quicksortmi(z,n)%mo*y%mo)%=mo;
return;
}
dfs(x+1,y,z,f);
dfs(x+1,lcm(y,c[x]),z+a[x],-f);
}*/
void dfs(int x,int y,int z,int v){
if (x==top+1){
(ans+=(ll)y*f[z]%mo*v%mo)%=mo;
return;
}
int i;
fo(i,0,a[x]){
dfs(x+1,i?lcm(y,c[x]):y,z+i,(ll)v*C[a[x]][i]%mo);
}
}
int main(){
C[0][0]=1;
fo(i,1,26){
C[i][0]=1;
fo(j,1,i) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mo;
}
scanf("%d",&ca);
while (ca--){
scanf("%d",&n);
f[1]=1;
fo(i,2,26){
f[i]=quicksortmi(i,n);
fo(j,1,i-1) (f[i]-=(ll)f[j]*C[i][j]%mo)%=mo;
}
fo(i,1,26){
ch=get();
b[i]=ch-'a'+1;
}
fo(i,1,26) pd[i]=bz[i]=0;
top=0;
fo(i,1,26)
if (!pd[i]){
k=b[i];
pd[i]=1;
t=1;
while (k!=i){
t++;
pd[k]=1;
k=b[k];
}
if (!bz[t]){
bz[t]=++top;
c[top]=a[top]=t;
}
else a[bz[t]]+=t;
}
ans=0;
dfs(1,1,0,1);
(ans+=mo)%=mo;
printf("%d\n",ans);
}
}