2019 2 14 總結
阿新 • • 發佈:2019-02-18
-m dfs its 方案 情況 所有 truct n) max
T1
考試的時候想到先把數字排序然後尋找連續的(通過差O(1)判斷)且編號是上升的然後卡在編號要上升這裏(於是就又出了兩道題),但是實際沒有這麽煩。
建一個map f
f[i]代表從頭到i這個數字最長的連續上升子序列是多少
轉移就是f[i]=f[i-1]+1
擴展出題:
有n個數字,每個數字有k種形態,每種形態大小為val
問1:給出m個詢問,問在區間l——r是否存在一種可能的序列為上升序列
1、當詢問較多k較小時,設f[i][j]代表當第i個數選取第j大的取值時,以該數字為結尾的上升序列最長為多少,每次查詢枚舉位置靠後的點的長度能否覆蓋較前那個點
復雜度O(nkk+mk)
2、詢問少k大的情況,貪心從l開始到r每次盡可能選取小且符合要求的數
復雜度O(nm)
問2:問從l到r合法的序列方案數為多少
從l開始dp,設f[i][j]代表當第i個數選取第j大的取值時,以該數字為結尾的上升序列方案數為多少,直接統計可轉移的狀態的和即可,復雜度O(nkk)
(都是水題)
#include<bits/stdc++.h> using namespace std; int n; int num[200009],nt[200009],val[200009]; bool mark[200009]; int ans,st; map<int,int> mp; inline int read() { int num=0,fs=1; char ch; while((ch<‘0‘||ch>‘9‘)&&ch!=‘-‘) ch=getchar(); if(ch==‘-‘) fs=-1,ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘) num=num*10+ch-‘0‘,ch=getchar(); return num*fs; } int main () { //freopen("sb.out","r",stdin); n=read(); for(int i=1;i<=n;i++) { num[i]=read(); mp[num[i]]=mp[num[i]-1]+1; if(mp[num[i]]>ans) ans=mp[num[i]],st=num[i]-ans+1; } printf("%d\n",ans); for(int i=1;i<=n&&ans;i++) { if(num[i]==st) printf("%d ",i),st++,ans--; } return 0; }
T2
核心思想:所有翻倍操作放在一個人身上一定最優
證明 若a2^n+b2^m>c^(n+m)
即在a2^n和b2^m中必有一個大於0.5*c^(n+m)
那將2^(n+m)移到它的身上肯定更值
然後枚舉誰翻倍,註意判斷b的情況即可
#include<bits/stdc++.h> using namespace std; inline long long read() { long long num=0,fs=1; char ch; while((ch<‘0‘||ch>‘9‘)&&ch!=‘-‘) ch=getchar(); if(ch==‘-‘) fs=-1,ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘) num=num*10+ch-‘0‘,ch=getchar(); return num*fs; } long long nb[21]; long long ans; long long n,a,b; struct gzw { long long x,y; }baby[200001]; inline long long calc(gzw x) { return x.x-x.y; } inline bool rnk(gzw a,gzw b) { return calc(a)>calc(b); } inline long long ksm(long long num,long long k) { return num*nb[k]; } int main () { nb[0]=1; for(long long i=1;i<=20;i++) { nb[i]=nb[i-1]*2; } n=read(),a=read(),b=read(); for(long long i=1;i<=n;i++) { baby[i].x=read(),baby[i].y=read(); } sort(baby+1,baby+n+1,rnk); for(long long i=1;i<=b;i++) { ans+=max(baby[i].x,baby[i].y); } for(long long i=b+1;i<=n;i++) { ans+=baby[i].y; } long long sum=ans; for (int i=1;i<=b;i++) ans=max(ans,sum-max(baby[i].x,baby[i].y)+(baby[i].x<<a)); sum=sum-max(baby[b].x,baby[b].y)+baby[b].y; for (int i=b+1;i<=n&&b;i++) ans=max(ans,sum-baby[i].y+(baby[i].x<<a)); printf("%I64d\n",ans); return 0; }
T3
k=1時就是普通的統計路徑數樹形DP即可
當k>1時就是(路徑長度和+每條路徑離被整除的差的和)/k
即長度為x的路徑額外貢獻就為k-x%k(當x=0時沒有貢獻)
cnt[i][j]代表在i節點包括它下面的節點中深度關於k的余數為j的節點數量
轉移就是
for(int x=0;x<k;x++)
{
for(int y=0;y<k;y++)
{
long long tmp=((x+y-(tot<<1))%k+k)%k;
tmp=((k-tmp)%k+k)%k;
ans+=tmp*cnt[now][x]*cnt[to[i]][y];
}
}
for(int x=0;x<k;x++)
{
cnt[now][x]+=cnt[to[i]][x];
}
別忘了賦初始值
sum[now]=cnt[now][tot%k]=1;
完整代碼
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int num=0,fs=1;
char ch;
while((ch<‘0‘||ch>‘9‘)&&ch!=‘-‘) ch=getchar();
if(ch==‘-‘) fs=-1,ch=getchar();
while(ch>=‘0‘&&ch<=‘9‘) num=num*10+ch-‘0‘,ch=getchar();
return num*fs;
}
int sb,n,k;
int head[200009],to[400009],nt[400009],bh;
long long cnt[200009][5],sum[200009];
long long ans;
inline void add(int u,int v)
{
bh++;
nt[bh]=head[u];
head[u]=bh;
to[bh]=v;
}
void dfs(int now,int fa,int tot)
{
sum[now]=cnt[now][tot%k]=1;
for(int i=head[now];i;i=nt[i])
{
if(to[i]!=fa)
{
dfs(to[i],now,tot+1);
sum[now]+=sum[to[i]];
ans+=sum[to[i]]*(n-sum[to[i]]);
for(int x=0;x<k;x++)
{
for(int y=0;y<k;y++)
{
long long tmp=((x+y-(tot<<1))%k+k)%k;
tmp=((k-tmp)%k+k)%k;
ans+=tmp*cnt[now][x]*cnt[to[i]][y];
}
}
for(int x=0;x<k;x++)
{
cnt[now][x]+=cnt[to[i]][x];
}
}
}
}
int main ()
{
n=read(),k=read();
for(int i=1;i<n;i++)
{
int u=read(),v=read();
add(u,v);
add(v,u);
}
dfs(1,0,0);
cout<<(ans/k);
return 0;
}
2019 2 14 總結