多校聯合第三場
1003 The Goddess Of The Moon
題意:如果字串存在某個字首與另一個字串的字尾相同,則這兩個字串匹配,求n種字串連成m個字串的連線方式。
dp[i][j]=
該dp表示式可以通過矩陣加速來實現
#include <iostream>
#include <cstdio>
#include <set>
#include<string.h>
#include <cstdlib>
#include<algorithm>
#include<math.h>
#include<vector>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
# define N 100010
const ll maxn = 100;
ll t,n,m,a[maxn],ans,cnt;
bool connect(ll a,ll b){
ll x[50],y[50],l1=0,l2=0;
while(a) { x[l1++]=a%10,a/=10; }
while(b) { y[l2++]=b%10,b/=10; }
ll l=min(l1,l2),tmp=0 ;
for(ll i=1;i<=l;i++){
bool flag=true;
for(ll j=0;j<i;j++){
if(x[j]!=y[l2-i+j]) { flag=false; break; }
}
if(flag) tmp=i;
}
if(tmp>=2) return true;
return false;
}
struct Mat{
ll ma[60][60];
void init() { memset(ma,0,sizeof(ma)); }
};
Mat A,B;
Mat operator * (const Mat &a,const Mat &b){
Mat c; c.init();
for(ll i=0;i<n;i++){
for(ll j=0;j<n;j++){
for(ll k=0;k<n;k++){
c.ma[i][j]=(c.ma[i][j]+(a.ma[i][k]*b.ma[k][j])%mod)%mod;
}
}
}
return c;
}
Mat quick(Mat a,int b){
Mat ans;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j) ans.ma[i][j]=1;
else ans.ma[i][j]=0;
}
}
while(b){
if(b&1) { ans=ans*a; b--; }
a=a*a,b>>=1;
}
return ans;
}
int main(){
//freopen("a.txt","r",stdin);
scanf("%lld",&t);
while(t--){
scanf("%lld%lld",&n,&m);
for(ll i=0;i<n;i++) scanf("%lld",&a[i]);
sort(a,a+n); cnt=0;
for(ll i=0;i<n;i++){
if(i<n-1&&a[i]!=a[i+1]) a[cnt++]=a[i];
}
a[cnt++]=a[n-1]; n=cnt;
A.init(),B.init();
for(ll i=0;i<n;i++){
for(ll j=0;j<n;j++){
if(connect(a[i],a[j])) B.ma[j][i]=1;
}
}
for(ll i=0;i<n;i++) A.ma[i][0]=1;
B=quick(B,m-1);
A=B*A;
ans=0;
for(ll i=0;i<n;i++) ans=(ans+A.ma[i][0])%mod;
printf("%lld\n",ans);
}
return 0;
}
1005 Fan Li
題意:求n個數中gcd相同的不相交區間的最大數量與對應的方 案數量
解法:
1.n個區間的最大不相交數量
解法:貪心。按區間結尾從小到大排序,取出第一個,然後取出滿足條件的最小的第二個,以此類推即可。
拓展:求n個區間中必須選取k個區間的最大不相交數量
解法:同上,只不過先記錄選取的k個區間的位置,在判斷時必須滿足區間不語這k個區間相交,相當於把區間分成了k+1份,分別進行貪心。
本題中先預處理出所有以第r個元素結尾gcd為x的連續區間,由於以第r個元素結尾的不同gcd個數之多為log(a[r]),因為以第r個元素結尾的區間gcd之間是約數關係,因此每次至少變化一個素因子p,a[r]至多log(a[r])個之因子,因此最多變化log(a[r])次。
設(l,r,x,pre)表示以第r個元素結尾gcd為x的區間左端點為pre,右端點為l
將上述資訊儲存在一個數組中,最多n*logn個四元組。
將上述四元組按照x大小排序,再按r大小排序。對於x相等的區間我們進行求解。
1.求最大不相交區間數量,由1中分析,可以貪心得到,但是求解方案數不能貪心,只能通過dp來求解。
設dp[i]:包含以第i個元素結尾的區間且gcd為x的最大不相交區間數量
sum[i]:前i個元素gcd為x的最大不相交區間數量
dp的求解過程可以通過貪心得到。首先如果dp[i]不遞增,那麼dp[i+1]不可能通過dp[i]得到,反證法:如果dp[i+1]通過dp[i]得到,說明r[i]< l[i+1],此時由於dp[i]不滿足右端點小於i+1的左端點的最大dp值,因此不能作為dp[i+1]的轉移值。所以只要記錄下遞增的dp值下標,然後每次更新dp時,只要二分查詢滿足r[j]<=l[i+1]的最大dp值,即最大r值即可作為dp[i+1]的最優轉移值
再求sum[i]
按照sum[j]對sum[i]的貢獻可以求完每個sum[j]後維護兩個樹狀陣列求sum[j]的前n項和以及j*sum[j]的前n項和,
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<vector>
#include<math.h>
#define Mod mod
using namespace std;
const int maxn = 100000+10;
int n,a[maxn];
const int mod = 998244353;
int gcd(int a,int b) { return b==0?a:gcd(b,a%b); }
struct no{
int l,r,x,pre;
bool operator<(const no &rhs)const{
if(x!=rhs.x) return x<rhs.x;
return r<rhs.r;
}
}q[maxn<<6];
int tot,nxt[maxn<<1];
int ans1,ans2;
int vt[maxn<<6],top;
int dp[maxn<<1],sum[maxn<<1];
int node[2][maxn];
int low(int x) { return x&(-x); }
int query(int id,int x) { int ans=0; for(int i=x;i>=1;i-=low(i)) ans=((ans+node[id][i])%mod+mod)%mod; return ans; }
void update(int id,int x,int p) { for(int i=x;i<=n;i+=low(i)) node[id][i]=((node[id][i]+p)%mod+mod)%mod; }
int bi(int l,int r,int a,int b){
int ret=-1;
while(l<=r){
int m=(l+r)>>1;
if(vt[m]<b) l=m+1;
else if(dp[vt[m]]>=a) ret=m,r=m-1;
else l=m+1;
}
return ret;
}
void fenjie(){
tot=0; int val[maxn];
q[tot++]=(no) { 1,1,a[1],1 };
nxt[1]=1,val[1]=a[1];
for(int i=2;i<=n;i++){
nxt[i]=i,val[i]=a[i];
int pos=i;
while(pos>0){
val[pos]=gcd(val[pos],a[i]);
while(nxt[pos]>1&&gcd(val[nxt[pos]-1],val[pos])==val[pos]){
nxt[pos]=nxt[nxt[pos]-1];
}
q[tot++]=(no){ pos,i,val[pos],nxt[pos] };
pos=nxt[pos]-1;
}
}
sort(q,q+tot);
}
void solve(){
ans1=ans2=0;
for(int i = 0;i < tot; i++) {
int top = 0;
int j = i;
while(j < tot && q[j].x == q[i].x) {
int pos = 0;
if(top > 0) {
pos = lower_bound(vt+1, vt+top+1, q[j].l)-vt;
pos--;
}
if(pos == 0) {
dp[q[j].r] = 1;
sum[q[j].r] = q[j].l-q[j].pre+1;
if(!top || dp[vt[top]] <= 1) {
vt[++top] = q[j].r;
update(0, q[j].r, sum[q[j].r]);
update(1, q[j].r, 1ll*sum[q[j].r]*q[j].r%Mod);
}
} else {
int mx = dp[vt[pos]];
dp[q[j].r] = dp[vt[pos]]+1;
sum[q[j].r] = 0;
int pos2 = bi(1, pos, mx, q[j].pre);
int pos3 = bi(1, pos, mx, 1);
if(pos2 != -1) {
int add = 1ll*q[j].l*(query(0, vt[pos])-query(0, vt[pos2]-1))%Mod;
if(add < 0) add += Mod;
add -= (query(1, vt[pos])-query(1, vt[pos2]-1)+Mod)%Mod;
if(add < 0) add += Mod;
sum[q[j].r] = add;
sum[q[j].r] += 1ll*(q[j].l-q[j].pre+1)*(query(0, vt[pos2]-1)-query(0, vt[pos3]-1)+Mod)%Mod;
if(sum[q[j].r] >= Mod) sum[q[j].r] -= Mod;
} else {
sum[q[j].r] += 1ll*(q[j].l-q[j].pre+1)*(query(0, vt[pos])-query(0, vt[pos3]-1)+Mod)%Mod;
if(sum[q[j].r] >= Mod) sum[q[j].r] -= Mod;
}
if(dp[vt[top]] <= dp[q[j].r]) {
vt[++top] = q[j].r;
update(0, q[j].r, sum[q[j].r]);
update(1, q[j].r, 1ll*sum[q[j].r]*q[j].r%Mod);
}
}
if(dp[q[j].r] > ans1) {
ans1 = dp[q[j].r];
ans2 = sum[q[j].r];
} else if(dp[q[j].r] == ans1) {
ans2 += sum[q[j].r];
if(ans2 >= Mod) ans2 -= Mod;
}
j++;
}
while(i < j) {
int x = q[i].r;
while(x <= n) {
node[0][x] = node[1][x] = 0;
x += x&-x;
}
i++;
}
i--;
}
printf("%d %d\n", ans1, ans2);
}
int main(){
//freopen("a.txt","r",stdin);
while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
fenjie();
solve();
}
return 0;
}
1006 Beautiful Set
題意:給n個數的集合
1.Make the set become a sequence, the beautiful value of the sequence is the sum of all the interval’s gcd(greatest common divisor), and the beautiful value of the set is the sum of the beautiful value of all the possible sequence.
ans1=
意思是選取k個數排列後插入原n-k個數的排列中有n-k+1種插入方法
按照gcd的值進行重新計算:設
ans1=
如果沒有後面的
回到原題
設h[i]=
ans1=