T1:
題解:
因爲限制了區間gcd相等,直接DP的話需要知道當前區間以及它的gcd:
Code(現在的人寫線段樹懶標記都不下傳的嗎。。。):
#include<bits/stdc++.h>
#define maxn 50005
#define fi first
#define se second
using namespace std;
const int mod = 998244353;
typedef pair<int,int> pii;
int n,m,tp,a[maxn];
pii S[maxn];
struct node{
int w,x,l,r,t;
bool operator < (const node &p)const{return w==p.w?x<p.x:w<p.w;}
}q[maxn*62];
int total,ans[maxn],f[maxn],g[maxn];
vector<int>P;
int sum[maxn<<2],tag[maxn<<2],ti[maxn<<2],tim;
int gcd(int a,int b){return b?gcd(b,a%b):a;}
inline void clr(int i){if(ti[i]!=tim) ti[i]=tim,sum[i]=tag[i]=0;}
void ins(int i,int l,int r,int x,int y,int v){
clr(i);
sum[i]=(sum[i]+1ll*(min(r,y)-max(l,x)+1)*v)%mod;//no pushdown!!! tql!!!
if(x<=l&&r<=y) {tag[i]=(tag[i]+v)%mod;return;}
int mid=(l+r)>>1;
if(x<=mid) ins(i<<1,l,mid,x,y,v);
if(y>mid) ins(i<<1|1,mid+1,r,x,y,v);
}
int qry(int i,int l,int r,int x,int y){
if(x>y||x>r||y<l) return 0;
clr(i);
if(x<=l&&r<=y) return sum[i];
int mid=(l+r)>>1;
return (1ll*(min(r,y)-max(l,x)+1)*tag[i]+qry(i<<1,l,mid,x,y)+qry(i<<1|1,mid+1,r,x,y))%mod;
}
void solve(int L,int R){
tim++;
for(int i=L;i<=R;i++){
if(q[i].t==0) ins(1,1,n,q[i].x,n,(qry(1,1,n,q[i].l-1,q[i].r-1)+q[i].r-q[i].l+1)%mod);
if(i==R||q[i].x!=q[i+1].x) f[q[i].x]=qry(1,1,n,q[i].x,q[i].x);
}
total=(total+f[q[R].x])%mod;
ans[q[R].x+1]=(ans[q[R].x+1]+f[q[R].x])%mod;//k+1...n
tim++;
for(int i=R;i>=L;i--){
if(q[i].t==1) ins(1,1,n,1,q[i].x,(qry(1,1,n,q[i].l+1,q[i].r+1)+q[i].r-q[i].l+1)%mod);
if(i==L||q[i].x!=q[i-1].x) g[q[i].x]=qry(1,1,n,q[i].x,q[i].x);
}
P.clear(),P.push_back(q[L].x);
for(int i=L+1;i<=R;i++) if(q[i].x!=q[i-1].x) P.push_back(q[i].x);
for(int i=0,pre=0,now,dt;i<P.size();i++,pre=now){//pre+1...now-1, now.
now=P[i];
dt=(1ll*(f[pre]+1)*(g[now]+1)+mod-1)%mod;
ans[pre+1]=(ans[pre+1]+dt)%mod,ans[now]=(ans[now]+mod-dt)%mod;
dt=(1ll*(f[pre]+1)*(g[i<P.size()-1?P[i+1]:n+1]+1)+mod-1)%mod;
ans[now]=(ans[now]+dt)%mod,ans[now+1]=(ans[now+1]+mod-dt)%mod;
}
}
int main()
{
//freopen("A.in","r",stdin);
//freopen("A.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
S[++tp]=pii(a[i],i);
for(int j=1;j<tp;j++) S[j].fi=gcd(S[j].fi,a[i]);
int tmp=tp; tp=1;
for(int j=2;j<=tmp;j++)
if(S[j].fi==S[tp].fi) S[tp].se=S[j].se;
else S[++tp]=S[j];
for(int j=1;j<=tp;j++) q[++m]=(node){S[j].fi,i,S[j-1].se+1,S[j].se,0};
}
tp=0,S[0].se=n+1;
for(int i=n;i>=1;i--){
S[++tp]=pii(a[i],i);
for(int j=1;j<tp;j++) S[j].fi=gcd(S[j].fi,a[i]);
int tmp=tp; tp=1;
for(int j=2;j<=tmp;j++)
if(S[j].fi==S[tp].fi) S[tp].se=S[j].se;
else S[++tp]=S[j];
for(int j=1;j<=tp;j++) q[++m]=(node){S[j].fi,i,S[j].se,S[j-1].se-1,1};
}
sort(q+1,q+1+m);
for(int i=1,j;i<=m;i=j+1){
for(j=i;j<m&&q[i].w==q[j+1].w;j++);
solve(i,j);
}
for(int i=1;i<=n;i++) ans[i]=(ans[i]+ans[i-1])%mod;
for(int i=1;i<=n;i++) printf("%d%c",(total-ans[i]+mod)%mod,i==n?10:32);
}
T2:
題解:
實際上可以直接建,因爲。
Code(注意此題按照題解的做法動態開點線段樹詢問時需要對一個完整區間詢問,因此訪問到當前點爲空時不能返回0):
#include<bits/stdc++.h>
#define maxn 100005
#define N 1073741823
#define lowbit(i) ((i)&-(i))
using namespace std;
int n,m,k,ans,rt,sz,lc[maxn*40],rc[maxn*40],cov[maxn*40],sx[maxn*40];
bool cnt[maxn*40];
struct node{int l,r,t;};
vector<node>q[maxn];
inline void pushup(int i,int l,int len){
cnt[i]=cov[i]?len&1:cnt[lc[i]]^cnt[rc[i]];
sx[i]=cov[i]?(lowbit(l)^(len>>1)):sx[lc[i]]^sx[rc[i]];
}
void ins(int &i,int l,int r,int x,int y,int v){
if(!i) i=++sz;
if(x<=l&&r<=y) {cov[i]+=v,pushup(i,l,r-l+1);return;}
int mid=(l+r)>>1;
if(x<=mid) ins(lc[i],l,mid,x,y,v);
if(y>mid) ins(rc[i],mid+1,r,x,y,v);
pushup(i,l,r-l+1);
}
bool Q_cnt(int i,int l,int r,int x,int y){
if(!i||x>r||y<l) return 0;
if(cov[i]) return (min(r,y)-max(l,x)+1)&1;
if(x<=l&&r<=y) return cnt[i];
int mid=(l+r)>>1;
return Q_cnt(lc[i],l,mid,x,y)^Q_cnt(rc[i],mid+1,r,x,y);
}
int Q_sx(int i,int l,int r,int x,int y,bool flg){
if(x>r||y<l) return 0;//!i don't return!!! Dynamic memory!
flg|=cov[i];
if(x<=l&&r<=y) return flg?(lowbit(l)^((r-l+1)>>1)):sx[i];
int mid=(l+r)>>1;
return Q_sx(lc[i],l,mid,x,y,flg)^Q_sx(rc[i],mid+1,r,x,y,flg);
}
int main()
{
scanf("%d%d%d",&k,&n,&m);
for(int i=1,a,b,c,d;i<=k;i++){
scanf("%d%d%d%d",&a,&b,&c,&d);
q[a].push_back((node){b,d,1}),q[c+1].push_back((node){b,d,-1});
}
for(int i=1;i<=n;i++){
for(int j=0;j<q[i].size();j++) ins(rt,0,N,q[i][j].l,q[i][j].r,q[i][j].t);
ans^=Q_cnt(rt,0,N,1,min(i,m))*lowbit(i);
if(i<m) ans^=Q_sx(rt,0,N,i+1,m,0);
}
printf("%d\n",ans);
}
T3:
題解:
因爲是求總數,可以考慮分開每一對逆序對乘上對應的方案數形成的貢獻,這樣就只需要記錄子樹中號節點排在拓撲序的第位時子樹拓撲序的方案數就可以DP了。
也有枚舉然後算在前面的方案數的方法。不過8太會。
據說是的,傳說可以做到。
Code:
#include<bits/stdc++.h>
#define maxn 505
#define rep(i,j,k) for(int i=j,lim=k;i<=lim;i++)
using namespace std;
const int mod = 1e9+7;
int n,siz[maxn],c[maxn][maxn],f[maxn][maxn][maxn],g[maxn],w[maxn],s[maxn][maxn],h[maxn][maxn];
//f[i][j][k]:in subtree of i point k is at the jth place, indicate the valid ways.
//g:reverse pairs in all ways. w:all ways.
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
inline void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
void merge(int x,int y){
int wx=w[x],wy=w[y],sx=siz[x],sy=siz[y];
g[x]=(1ll*g[x]*wy+1ll*g[y]*wx)%mod*c[sx+sy-1][sy]%mod;
w[x]=1ll*wx*wy%mod*c[sx+sy-1][sy]%mod;
rep(i,1,sx) rep(j,1,n) s[i][j]=h[i][j]=f[x][i][j],f[x][i][j]=0;
rep(i,1,sx) rep(j,1,n) s[i][j]=(s[i][j]+s[i][j-1])%mod;
rep(i,1,sx) rep(j,1,n) s[i][j]=(s[i][j]+s[i-1][j])%mod;
rep(p,1,sy) rep(i,1,n) if(f[y][p][i]) rep(q,1,sx){
int t=1ll*f[y][p][i]*c[p+q-2][p-1]%mod*c[sx-q+sy-p][sy-p]%mod;
f[x][p+q][i]=(f[x][p+q][i]+1ll*t*wx)%mod;
g[x]=(g[x]+t*(1ll*s[q][i-1]+s[sx][n]-s[sx][i]-s[q][n]+s[q][i]))%mod;
}
rep(p,1,sx) rep(i,1,n) if(h[p][i]) rep(q,0,sy){
if(p==1&&q) break;
int t=1ll*c[sx-p+sy-q][sy-q]*h[p][i]%mod;
if(p>1) t=1ll*t*c[p-2+q][q]%mod;
f[x][p+q][i]=(f[x][p+q][i]+1ll*t*wy)%mod;
}
siz[x]+=siz[y];
}
void dfs(int u,int ff){
f[u][1][u]=1,g[u]=0,w[u]=1,siz[u]=1;
for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff) dfs(v,u),merge(u,v);
}
int main()
{
scanf("%d",&n);
for(int i=(c[0][0]=1);i<=n;i++)
for(int j=(c[i][0]=1);j<=i;j++)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),line(x,y),line(y,x);
dfs(1,0);
printf("%d\n",(g[1]+mod)%mod);
}