D
比賽時的思路是計數,沒有想到DP,現在一想其實DP的挺明顯的。
這個思路是網上一個我看的比較明白的思路。
題解:
1. 直接把整個環看成相同數目的黑白段的連接(必定是相同數目)。
2. 用DP計算將i個數分解成j段的所有分發的乘積和,,dp的時候用前綴和和滾動數組優化。
3.拼接的時候後取模。
#include<bits/stdc++.h>
#define MOD 1000000007
#define N 5005
#define ll long long
using namespace std;
ll qpow(ll x,ll t){ return t==0?1LL:qpow((x*x)%MOD,t>>1)*(t%2?x:1)%MOD; }
ll inv[N];
ll dp[N][N];
ll s1[2][N];
ll s2[2][N];
void init(){
int n=5000;
for (int i=1;i<=n;i++) inv[i]=qpow(i,MOD-2);
for (int i=1;i<=n;i++){ //geshu
dp[i][1]=i;
s1[i%2][1]=(s1[1-i%2][1]+dp[i][1])%MOD;
s2[i%2][1]=(s2[1-i%2][1]+dp[i][1]*(n-i+1) )%MOD;
for (int j=2;j<=i;j++){
dp[i][j]=((s2[1-i%2][j-1]-s1[1-i%2][j-1]*(n-i+1))%MOD+MOD)%MOD;
s1[i%2][j]=(s1[1-i%2][j]+dp[i][j]);
s2[i%2][j]=(s2[1-i%2][j]+dp[i][j]*(n-i+1))%MOD;
}
}
}
int main(){
init();
ll n,m;
while (~scanf("%lld%lld",&n,&m)){
ll ans=0;
if (n>m) swap(n,m);
for (int i=1;i<=n;i++){
ans=(ans+dp[n][i]*dp[m][i]%MOD*(n+m)%MOD*inv[i]%MOD)%MOD;
}
printf("%lld\n",ans);
}
return 0;
}
E
比賽的時候想用樹鏈剖分,寫了快兩百行代碼沒debug出來比賽結束,賽後發現直接用並查集維護就可以了。
比賽時已經發現的規律:每個邊都只會處理一次,因此直接用異或的性質來處理z的改變。
直接每次沿路徑向上即可,沿路合併,修改z值。
debug很久的原因是:加邊順序錯了……暈
#include<bits/stdc++.h>
#define N 5005
#define MAXLOGV 21
using namespace std;
struct edge{
int v,next;
}e[N*2];
int tot=0;
int fa[N][MAXLOGV],h[N],dep[N],s[N],z;
int F[N];
void add(int u, int v){
e[++tot]=(edge){v,h[u]};
h[u]=tot;
}
void dfs(int u,int f)
{
fa[u][0]=f;
dep[u]=dep[f]+1;
if (f) s[u]=1;
else s[u]=0;
for(int i=h[u];~i;i=e[i].next){
int v=e[i].v;
if(v==f) continue;
dfs(v,u);
s[u]++;
}
z^=s[u];
}
void LCA_init(int n){
memset(fa,0,sizeof(fa));
dep[0]=0;
dfs(1,0);
for(int k=0;k+1<MAXLOGV;++k){
for(int v=1;v<=n;++v){
if(fa[v][k]==0) fa[v][k+1]=0;
else fa[v][k+1]=fa[fa[v][k]][k];
}
}
}
int LCA(int u,int v)
{
if(dep[u]>dep[v]) swap(u,v);
for(int k=MAXLOGV-1;k>=0;k--){ //改了順序
if( (dep[v]-dep[u])>>k&1){
// cout<<fa[v][k]<<endl;
v=fa[v][k];
}
}
if(u==v) return u;
for(int k=MAXLOGV-1;k>=0;--k){
if(fa[u][k]!=fa[v][k]){
u=fa[u][k];
v=fa[v][k];
}
}
return fa[u][0];
}
int get(int x){
return F[x]==x?x:F[x]=get(F[x]);
}
int unit(int x, int y){
int fx=get(x);
int fy=get(y);
F[fx]=fy;
}
void work(int x, int y){
// cout<<"merge "<<x<<" "<<y<<endl;
int fx=get(x);
int fy=get(y);
while (fx!=fy){
// cout<<"fx "<<fx<<" fy "<<fy<<endl;
if (fa[fx][0]!=y){
// cout<<"#"<<fa[fx][0]<<" "<<s[fa[fx][0]]<<"->"<<s[fa[fx][0]]-1<<endl;
if (dep[fx]<=dep[fy]) while (1);
z^=s[fa[fx][0]];
s[fa[fx][0]]--;
z^=s[fa[fx][0]];
unit(fx,fa[fx][0]);
fx=get(fx);
}
else{
break;
}
}
}
int main(){
int n,m,a,b,x,y,u,v;
while (scanf("%d%d%d%d%d%d",&n,&m,&a,&b,&x,&y)!=EOF){
memset(h,-1,sizeof(h));
tot=0;
z=0;
for (int i=1;i<=n;i++) F[i]=i;
for (int i=1;i<=n-1;i++){
scanf("%d%d",&u,&v);
add(u+1,v+1);
add(v+1,u+1);
}
LCA_init(n);
for (int i=1;i<=m;i++){
int nx=(a*x+b*y+z)%n;
int ny=(b*x+a*y+z)%n;
// cout<<"nxny "<<nx<<" "<<ny<<" "<<z<<endl;
x=nx;
y=ny;
work(x+1,LCA(x+1,y+1));
}
printf("%d %d\n",x,y);
}
return 0;
}
/*
5 10 5 5 4 0
0 1
1 2
2 3
3 4
2 2
5 25 1 1 3 0
0 1
0 2
1 3
1 4
*/
I
這是短期內第二次使用線段樹超時了,我無話可說。
改用用了的記錄方法,記錄每個數字的情況,不會超時。
思路:
1. 先處理以每個數字爲結尾的最長上升子序列,再處理以每個數字爲開頭的最長上升子序列。
2. 然後從後往前,用一個數組保存後面的數字的,最長上升子序列爲i的序列的開頭最大是多少(爲了讓答案最優),每找到一個0更新這個零到之前找到的最近的一個零之前的所有情況進數組。
3. 設除去0以外的最長上升子序列的長度爲,對於非零的第i個數,的數字都可以讓這個序列的最長上升子序列加一。在t數組裏標記一下就可以了。
4.注意考慮只有0的情況!!!!!!!!!!!!!!!!
#include<bits/stdc++.h>
using namespace std;
#define N 100005
int a[N],len1[N],len2[N],ans[N],l[N],t[N];
/*
struct tree{
int t[N*4],tag[N*4];
void clear(){
memset(t,0,sizeof(t));
memset(tag,0,sizeof(tag));
}
void push_down(int k, int l, int r, int m){
tag[k*2]=tag[k*2+1]=1;
t[k*2]=m-l+1;
t[k*2+1]=r-m;
tag[k]=0;
}
void update(int k){
t[k]=t[k*2]+t[k*2+1];
}
void insert(int k, int l, int r, int left, int right){
if (l>right||r<left) return;
if (left<=l&&r<=right){
tag[k]=1;
t[k]=r-l+1;
return ;
}
int m=(l+r)/2;
if (tag[k]) push_down(k,l,r,m);
insert(k*2,l,m,left,right);
insert(k*2+1,m+1,r,left,right);
update(k);
}
int query(int k, int l, int r, int left, int right){
if (l>right||r<left) return 0;
if (left<=l&&r<=right)return t[k];
int m=(l+r)/2;
if (tag[k]) push_down(k,l,r,m);
return query(k*2,l,m,left,right)+query(k*2+1,m+1,r,left,right);
}
}t;
*/
int main(){
int n;
while (scanf("%d",&n)!=EOF){
memset(t,0,sizeof(t));
memset(l,-1,sizeof(l));
memset(ans,0,sizeof(0));
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int len=0;
for (int i=1;i<=n;i++){
if (a[i]==0){
continue;
}
if (a[i]>ans[len]){
ans[++len]=a[i];
len1[i]=len;
}
else{
int pos=lower_bound(ans,ans+len+1,a[i])-ans;
len1[i]=pos;
ans[pos]=a[i];
}
}
bool flag=false;
long long tmp=1e7;
for (int i=n;i>=1;i--){
if (a[i]==0) flag=true;
if (a[i]!=0&&len1[i]==len&&flag){
tmp=min((long long)a[i],tmp);
}
}
if (tmp<n){
// cout<<"1+"<<tmp+1<<" "<<n<<endl;
t[tmp+1]++;
t[n+1]--;
}
len=0;
ans[0]=-1e7;
for (int i=n;i>=1;i--){
if (a[i]==0){
len2[i]=len;
continue;
}
if (-a[i]>ans[len]){
ans[++len]=-a[i];
len2[i]=len;
}
else{
int pos=lower_bound(ans,ans+len+1,-a[i])-ans;
len2[i]=pos;
ans[pos]=-a[i];
}
//cout<<len2[i]<<endl;
}
// cout<<"len"<<len<<endl;
int lst=-1;
for (int i=n;i>=1;i--){
if (a[i]==0){
for (int j=i+1;j<=(lst==-1?n:lst-1);j++){
l[len2[j]]=max(l[len2[j]],a[j]);
// cout<<len2[j]<<"->"<<l[len2[j]]<<endl;
}
lst=i;
if (l[len]>1){
// cout<<"2+"<<1<<" "<<l[len]-1<<endl;
t[1]++;
t[l[len]]--;
}
}
else{
if (lst!=-1&&l[len-len1[i]]>a[i]+1){
// cout<<"3+"<<a[i]+1<<" "<<l[len-len1[i]]-1<<endl;
t[a[i]+1]+=1;
t[l[len-len1[i]]]-=1;
}
}
}
if (len==0){
cout<<(n+1)*n/2<<endl;
}
else{
long long cnt=0;
tmp=0;
for (long long i=1;i<=n;i++){
tmp+=t[i];
// cout<<"tmp"<<tmp<<endl;
cnt+=i*(len+(tmp>0?1:0));
}
printf("%lld\n",cnt);
}
}
}
H沒找到題解且不會寫,待補