6102
天天爱跑步
毒瘤题,神仙树上上差分。
先推出两个式子
\(dep[u] = dep[i] + w[i]\),
\(dep[u] - dep[lca] = w[i] - dep[i]\) 开桶差分。
因为差分会导致统计子树时会有子树外的值,所以要用前缀作差的形式来消除影响。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<set>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 7;
int n,m;
int l,pre[maxn<<1],last[maxn],other[maxn<<1];
int sum[maxn],tmp[maxn],w[maxn],ans[maxn],dep[maxn];
int f[maxn][20];
int c1[maxn<<1],c2[maxn<<1];
vector <int> a1[maxn],b1[maxn],a2[maxn],b2[maxn];
void add(int x,int y)
{
l++;
pre[l] = last[x];
last[x] = l;
other[l] = y;
}
void dfs2(int x)
{
for(int p = last[x];p;p = pre[p])
{
int v = other[p];
if(v == f[x][0])continue;
dep[v] = dep[x] + 1;
f[v][0] = x;
dfs2(v);
}
}
int lca(int u,int v)
{
if(dep[u] < dep[v])swap(u,v);
for(int i = 0;i <= 16;i ++)
{
if((dep[u] - dep[v])&(1<<i))u = f[u][i];
}
if(u == v)return u;
for(int i = 17;i >= 0;i --)
{
if(f[u][i] != f[v][i])
{
u = f[u][i];
v = f[v][i];
}
}
return f[u][0];
}
void dfs(int x,int fa){
int cnt1 = c1[dep[x] + w[x]],cnt2 = c2[w[x] - dep[x] + n];
for(int p = last[x];p;p = pre[p]){
int v = other[p];
if(v == fa)continue;
dfs(v,x);
}
for(int i = 0;i < a1[x].size();i ++)c1[a1[x][i]] ++;
for(int i = 0;i < b1[x].size();i ++)c1[b1[x][i]] --;
for(int i = 0;i < a2[x].size();i ++)c2[a2[x][i] + n] ++;
for(int i = 0;i < b2[x].size();i ++)c2[b2[x][i] + n] --;
ans[x] = c1[dep[x] + w[x]] - cnt1 + c2[w[x] - dep[x] + n] - cnt2;
}
int main()
{
n = read(),m = read();
for(int i = 1;i < n ;i ++)
{
int u = read(),v = read();
add(u,v);add(v,u);
}
dfs2(1);
for(int j = 1;j <= 18;j ++){
for(int i = 1;i <= n ;i ++){
f[i][j] = f[f[i][j-1]][j-1];
}
}
for(int i = 1;i <= n ;i ++)w[i] = read();
for(int i = 1;i <= m ;i ++){
int u = read(),v = read();
int x = lca(u,v);
a1[u].push_back(dep[u]);
b1[f[x][0]].push_back(dep[u]);
a2[v].push_back(dep[u] - 2*dep[x]);
b2[x].push_back(dep[u] - 2*dep[x]);
}
dfs(1,0);
for(int i = 1;i <= n;i ++)cout<<ans[i]<<" ";
return 0;
}
蚯蚓
发现题目隐藏性质,减低复杂度的关键在于大根堆的log,每次切断都是单调的,便可用三个队列维护。
证明:两条蚯蚓,长度为\(a,b(a > b)\),\(t\)秒后切断\(b\),此时a的两条的长度\(pa + tx,(1-p)x + tx\),\(b\)两条长度\(p(b + t),(1-p)(b + t)\)单调显而易见。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<set>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 7e6 + 7;
queue <ll> q1,q2,q3;
int n,m,f,u,v,t;
int a[maxn];
ll addtag;
int main()
{
scanf("%d%d%d%d%d%d",&n,&m,&f,&u,&v,&t);
double p = (double)u/(double)v;
for(int i = 1;i <= n;i ++){
scanf("%d",a + i);
}
sort(a + 1,a + 1 + n);
for(int i = n;i >= 1;i --)
q1.push(a[i]);
for(int i = 1;i <= m;i ++){
ll mx = -1e9,flag = 0;
if(!q1.empty() && q1.front() > mx) mx = q1.front(),flag = 1;
if(!q2.empty() && q2.front() > mx) mx = q2.front(),flag = 2;
if(!q3.empty() && q3.front() > mx) mx = q3.front(),flag = 3;
if(flag == 1) q1.pop();
if(flag == 2) q2.pop();
if(flag == 3) q3.pop();
if(i % t == 0){
printf("%lld ",mx + addtag);
}
mx += addtag;
ll x = floor(p * mx);ll y = mx - x;
x -= addtag;y -= addtag;
q2.push(x - f);q3.push(y - f);
addtag += f;
}
cout<<endl;
for(int i = 1;i <= n + m;i ++){
ll mx = -1e9,flag = 0;
if(!q1.empty() && q1.front() > mx) mx = q1.front(),flag = 1;
if(!q2.empty() && q2.front() > mx) mx = q2.front(),flag = 2;
if(!q3.empty() && q3.front() > mx) mx = q3.front(),flag = 3;
if(flag == 1) q1.pop();
if(flag == 2) q2.pop();
if(flag == 3) q3.pop();
if(i % t == 0){
printf("%lld ",mx + addtag);
}
}
return 0;
}
愤怒的小鸟
\(dp\)还是比较好想的,预处理出所有抛物线,\(dp[s | line[i]] = max(dp[s] + 1)\)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<set>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;
int n,m,T,ans;
double x[1010],y[1011];
int dp[(1<<18) + 1],line[10101],cnt;
bool calc(int d,double a,double b){
// cout<<abs(a*x[d]*x[d] + b*x[d] - y[d])<<endl;
if(fabs(a*x[d]*x[d] + b*x[d] - y[d]) <= 0.000001)return 1;
return 0;
}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i = 1;i <= n ;i ++){
scanf("%lf%lf",&x[i],&y[i]);
}
memset(dp,0x3f,sizeof dp);
dp[0] = 0;
cnt = 0;
for(int i = 1;i <= n ;i ++){
line[++cnt] = (1 << (i-1));
for(int j = i + 1;j <= n;j ++){
if(fabs(y[i]/x[i] - y[j]/x[j]) <= 0.0001)continue;
double a = (x[j]*y[i] - x[i]*y[j])/((x[i]-x[j])*x[i]*x[j]);
double b = (y[i] - a*x[i]*x[i])/x[i];
if(a > 0)continue;
int s = 0;
for(int k = 1;k <= n ;k ++){
if(calc(k,a,b)){
s |= (1 << (k-1));
}
}
line[++cnt] = s;
}
}
for(int s = 0;s < (1 << n);s ++){
for(int i = 1;i <= cnt;i ++){
dp[s | line[i]] = min(dp[s] + 1,dp[s|line[i]]);
}
}
printf("%d\n",dp[(1 << n)-1]);
}
return 0;
}
换教室
一个期望\(DP\)入门题,\(dp[i][j][0/1]\)表示前\(i\)个改了\(j\)门,第\(i\)门改/没改的期望,转移比较长要仔细写。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<set>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;
int read(){
int x;scanf("%d",&x);return x;
}
const int maxn = 2001;
int n,m,v,e,c[maxn],d[maxn];
double dis[maxn][maxn];
double dp[maxn][maxn][2],k[maxn];
int main()
{
n = read(),m = read(),v = read(),e = read();
for(int i = 1;i <= n;i ++)c[i] = read();
for(int i = 1;i <= n;i ++)d[i] = read();
for(int i = 1;i <= n;i ++)scanf("%lf",&k[i]);
for(int i = 1;i <= v;i ++){
for(int j = 1;j <= v;j ++){
dis[i][j] = 1e9 + 7;
}
}
for(int i = 1;i <= e;i ++){
int a = read(),b = read();double w;
scanf("%lf",&w);
dis[a][b] = dis[b][a] = min(dis[a][b],w);
}
for(int i = 1;i <= v;i ++)dis[i][i] = 0;
for(int p = 1;p <= v;p ++){
for(int i = 1;i <= v;i ++){
for(int j = 1;j <= v;j ++)
dis[i][j] = min(dis[i][j],dis[i][p]+dis[p][j]);
}
}
for(int i = 0;i <= n ;i ++){
for(int j = 0 ;j <= m ;j ++){
dp[i][j][0] = dp[i][j][1] = 1e9 + 7;
}
}
dp[1][0][0] = dp[1][1][1] = 0;
double ans = 1e9 + 7;
for(int i = 2;i <= n ;i ++){
for(int j = 0;j <= min(m,i) ;j ++){
double p1 = k[i],p2 = 1-k[i],p3 = k[i-1],p4 = 1 - k[i-1];
dp[i][j][0] = min(dp[i-1][j][0] + dis[c[i-1]][c[i]],dp[i-1][j][1] + dis[d[i-1]][c[i]]*p3 + dis[c[i-1]][c[i]]*p4);
if(j)
dp[i][j][1] = min(dp[i-1][j-1][0] + dis[c[i-1]][c[i]]*p2 + dis[c[i-1]][d[i]]*p1,dp[i-1][j-1][1] + dis[c[i-1]][c[i]]*p2*p4 + dis[c[i-1]][d[i]]*p4*p1 + dis[d[i-1]][c[i]]*p3*p2 + dis[d[i-1]][d[i]]*p3*p1);
// printf("%.2lf %.2lf %d %d\n",dp[i][j][1],dp[i][j][0],i,j);
}
}
for(int i = 0;i <= m ;i ++){
ans = min(min(dp[n][i][0],dp[n][i][1]),ans);
}
printf("%.2lf",ans);
return 0;
}
原文地址:https://www.cnblogs.com/wtz2333/p/12233125.html
时间: 2024-11-15 01:18:04