DAY 4
zhx出题
裂变链接
【问题描述】
你是能看到第一题的 friends 呢。
——hja
众所周知,小葱同学擅长计算,尤其擅长计算组合数,但这个题和组合数没什么关系。
现在有????个异构体在前线排成一排参与战斗,但由于地方火力过于凶猛,异构体们都受到了不同程度的损伤。为了能够让所有异构体继续战斗,避免由于能量不均衡导致的爆炸,异构体们需要使得他们彼此之间的能量值一样。记第????个异构体当前????????的能量,每个时刻,每个异构体都可以做如下三种操作中的一种:
1、传递1的能量给自己左边相邻的异构体(如果存在)。
2、传递1的能量给自己右边相邻的异构体(如果存在)。
3、传递1的能量给自己(摸鱼)。
为了尽快的回到前线作战,异构体们希望在最短的时间内使得所有异构体的能量值一样,问最短时间。数据保证有解。操作过程中自己的能量可以变为负数。
【输入格式】
第一行一个整数????代表异构体数量。
接下来一行????个整数代表能量值。
【输出格式】
输出一行一个数代表答案。
【样例输入】
3
1 0 5
【样例输出】
3
【数据规模与约定】
对于30%的数据,???? ≤ 10, |????????| ≤ 10。
对于60%的数据,???? ≤ 100。
对于另外20%的数据,能量值的序列一定先单调递增再单调递减。
对于100%的数据,1 ≤ ???? ≤ 10^5, |????????| ≤ 10^5。
题解
确定一个异构体在最优情况下传递的能量
每一个都达到v
前缀和表示总能量
因为都要变成v,所以前i-1总能量应该是(i-1)*v
也就是说i不需要向左边输出能量
右边最后的能量值应该是(n-i)*v
节点的作用就是把左边的能量的差值全部转移到右边去,这个差值就是它传递的能量
枚举每一个节点
总共四种情况
1.左边多,右边少
2.左边少,右边多
3.左边多,右边多,说明i自己少 此时需要两边与期望能量差值的max
4.左边少,右边少,说明i自己多 此时需要两边与期望能量差值的和
然后取max就行了
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int maxn=100010; int n,z[maxn]; long long sum[maxn]; void read() { scanf("%d",&n); for (int a=1;a<=n;a++) scanf("%d",&z[a]); } long long work() { for (int a=1;a<=n;a++) sum[a] = sum[a-1]+z[a]; if (sum[n]%n) return -1; long long ans=0,v=sum[n]/n; for (int a=1;a<=n;a++) { long long l=sum[a-1]-1ll*(a-1)*v; long long r=sum[n]-sum[a]-1ll*(n-a)*v; if (l<0 && r<0) ans=max(ans,-l-r); ans=max(ans,max(abs(l),abs(r))); } return ans; } int main() { read(); printf("%lld\n",work()); return 0; }
T2
死亡鸽者
【问题描述】
你是能看到第二题的 friends 呢。
——aoao
众所周知,小葱同学擅长计算,尤其擅长计算组合数,但这个题和组合数没什么关系。
死亡鸽者最喜欢的事情就是咕咕咕,他的座右铭“风萧萧兮易水寒,壮士一去兮不复返,然鸽子至今未到”也每天被他所忘记。今天,死亡鸽者又要开始咕咕的旅行了。现在有????座城市排成一排,死亡鸽者会从第一座城市一直走到最后一座城市。每个城市都有一个数????????,每次死亡鸽者可以选择取走或者不取走这个数,但想要取走这个数的话要求这个数必须是所有已经取走的数的倍数或者约数。现在问死亡鸽者从第一座城市走到最后一座城市的过程中,最多取走多少个数。
【输入格式】
第一行一个数????。
接下来一行????个数代表每个城市的数。
【输出格式】
一行一个数代表答案。
【样例输入】
3
2 6 3
【样例输出】
2
【数据规模与约定】
对于20%的数据,???? ≤ 10。
对于50%的数据,???? ≤ 1000。
对于另外20%的数据,所有城市的数不重复。
对于100%的数据,1 ≤ ????, ???????? ≤ 10^6。
题解
从左向右走?其实没啥用,答案和这个没有关系
可以把这些数从小到大排序,这样就只用考虑这个数是不是前边的数的倍数就行了
最长上升子序列
F[i]表示选择a[i]的情况下序列长度最长是多少
转移就是枚举它前面的数
只需要保证a[i]是a[j]的倍数就行了,因为a[j]一定已经是前边的数的倍数了
f[i]=max(f[j])+1 a[i]|a[j]
50pts
如果没有重复的
答案的长度不超过logn
那么就可以去重,因为只要一个重复的选上,其他就必须选
去重之后跑搜索也可以过
或者把dp在去重之后的数组上做,也可以过
100pts
正解?
所有的数<=10^6这个条件还没有用过
可以开10^6个桶记录每个数出现了多少次
用f[i]表示现在取出的序列在最后一个数为i的情况下最长有多长
现在取出的数是i,那么之后的都是i的倍数
f[k*i]=max(f[i]+cnt[k*i])
我们并不关心每个数是多少,只关心有多少个
#include<cstdio> #include<cstdlib> #include<cstring> using namespace std; const int maxn=1000010; int n,cnt[maxn],f[maxn]; int main() { scanf("%d",&n); for (int a=1;a<=n;a++) { int v; scanf("%d",&v); cnt[v]++; f[v]++; } int ans=0; for (int a=1;a<=1000000;a++) if (f[a]) { if (f[a]>ans) ans=f[a]; for (int b=a+a;b<=1000000;b+=a) if (cnt[b] && f[a]+cnt[b]>f[b]) f[b]=f[a]+cnt[b]; } printf("%d\n",ans); return 0; }
T3
进阶之灾
【问题描述】
你是能看到第三题的 friends 呢。
——laekov
众所周知,小葱同学擅长计算,尤其擅长计算组合数,但这个题和组合数没什么关系。
TarjanLusa 是一款风靡全球的卡牌游戏,在这款游戏中,你需要一层一层的前进,击败像萌死戳、天启骑士等 boss,最终来到心脏面前一决胜负。为了能够有一套更好的卡牌来面对心脏,我们需要在每一层选择更好的卡牌。假设我们总共有????层,在第????层的时候,我们可以从两张卡牌中选择一张加入我们的卡组,这两张卡牌的战斗力分别为????????, ????????。在经过????层的选择之后,我们便会有一套????张卡的卡组,而整套卡组的战斗力取决于卡牌与卡牌之间战斗力差值的绝对值的最小值。但是心脏是一个非常强大的敌人,如果我们不能拥有强大的战斗力,人类就会一败涂地。所以,现在我们想知道,战斗力最大可能是多少。
【输入格式】
第一行一个整数????。
接下来????行每行两个整数????????, ????????。
【输出格式】
输出一行一个数代表答案。
【样例输入】
3
1 2
3 4
5 6
【样例输出】
2
【数据规模与约定】
对于20%的数据,1 ≤ ???? ≤ 10。
对于50%的数据,1 ≤ ???? ≤ 100。
对于另外20%的数据,所有卡牌战斗力的最大值减去最小值小于等于100。
对于100%的数据,1 ≤ ???? ≤ 10^5, 1 ≤ ????????, ???????? ≤ 10^9
题解
2-SAT?
然鹅不会写
最小值最大化
二分
看看能否构造出战斗力大于等于v的卡牌组
现在有a1,b1和a2,b2
如果|a1-a2|,那么a1,a2不能同时选,从a1向b2连边
同理,从a2向b2连边
直接n^2枚举卡牌,然后跑2-SAT
50pts
不能过100pts的原因
原因:枚举建边是n^2的
60~70 pts 直接输出0 ??? 抽屉原理...我吐了
发现边太多了,我们想尽量节省边的数量
线段树优化建图(大雾)
把a1b1,a2b2...anbn拿出来从小到大排序
c1,c2,...,c2n
考虑ci,一定对应原来的某个数
两条边之间建边的条件是|ai-aj|<v
也就是ci前面一段和ci后面一段,是连续的区间
李姐为是区间加边的操作
线段树
如果第五个节点排好序之后是a2 如果有一个冲突的,一定和b2连边
那么在a2这个节点下面挂一个b2
区间加边
假如a3想对2-6之间的节点加边
2-6拆成[2,2],[3,4],[5,6]
从a3对这三个区间加边
连通性没有发生改变
图和原来的是等价的
Zhxtql
所以边数是n log^2 n
然后跑tarjan
#include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<algorithm> using namespace std; const int BUF_SIZE = 30; char buf[BUF_SIZE], *buf_s = buf, *buf_t = buf + 1; #define PTR_NEXT() \ { \ buf_s ++; \ if (buf_s == buf_t) \ { \ buf_s = buf; \ buf_t = buf + fread(buf, 1, BUF_SIZE, stdin); \ } \ } #define readint(_n_) \ { \ while (*buf_s != '-' && !isdigit(*buf_s)) \ PTR_NEXT(); \ bool register _nega_ = false; \ if (*buf_s == '-') \ { \ _nega_ = true; \ PTR_NEXT(); \ } \ int register _x_ = 0; \ while (isdigit(*buf_s)) \ { \ _x_ = _x_ * 10 + *buf_s - '0'; \ PTR_NEXT(); \ } \ if (_nega_) \ _x_ = -_x_; \ (_n_) = (_x_); \ } #define readstr(_s_) \ { \ while (!isupper(*buf_s)) \ PTR_NEXT(); \ char register *_ptr_ = (_s_); \ while (isupper(*buf_s) || *buf_s == '-') \ { \ *(_ptr_ ++) = *buf_s; \ PTR_NEXT(); \ } \ (*_ptr_) = '\0'; \ } #define readlonglong(_n_) \ { \ while (*buf_s != '-' && !isdigit(*buf_s)) \ PTR_NEXT(); \ bool register _nega_ = false; \ if (*buf_s == '-') \ { \ _nega_ = true; \ PTR_NEXT(); \ } \ long long register _x_ = 0; \ while (isdigit(*buf_s)) \ { \ _x_ = _x_ * 10 + *buf_s - '0'; \ PTR_NEXT(); \ } \ if (_nega_) \ _x_ = -_x_; \ (_n_) = (_x_); \ } #define wmt 1,(n<<1),1 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 const int maxn=100010; const int maxp=maxn+(maxn<<2); const int maxm=maxn+maxp+maxn*20; int n,size,cnt,en,t,dfn[maxp],low[maxp],s[maxp],belong[maxp],pos[maxn]; bool instack[maxp]; struct edge { int e; edge *next; }*v[maxp],ed[maxm]; void add_edge(int s,int e) { en++; ed[en].next=v[s];v[s]=ed+en;v[s]->e=e; } struct rec { int v,p; rec(){} rec(int a,int b) { v=a;p=b; } }z[maxn]; bool operator<(const rec &a,const rec &b) { return a.v<b.v; } void dfs(int p) { t++; dfn[p]=low[p]=t; instack[p]=true; s[++size]=p; for (edge *e=v[p];e;e=e->next) if (!dfn[e->e]) { dfs(e->e); low[p]=min(low[p],low[e->e]); } else { if (instack[e->e]) low[p]=min(low[p],dfn[e->e]); } if (dfn[p]==low[p]) { cnt++; while (s[size]!=p) { belong[s[size]]=cnt; instack[s[size]]=false; size--; } belong[p]=cnt; instack[p]=false; size--; } } void build(int l,int r,int rt) { if (l==r) { add_edge(rt+(n<<1),z[l].p<=n?z[l].p+n:z[l].p-n); return; } int m=(l+r)>>1; build(lson); build(rson); add_edge(rt+(n<<1),(rt<<1)+(n<<1)); add_edge(rt+(n<<1),(rt<<1|1)+(n<<1)); } void insert(int l,int r,int rt,int nowl,int nowr,int p) { if (nowl<=l && r<=nowr) { add_edge(p,rt+(n<<1)); return; } int m=(l+r)>>1; if (nowl<=m) insert(lson,nowl,nowr,p); if (m<nowr) insert(rson,nowl,nowr,p); } bool check(int k) { en=0;cnt=0; memset(v,0,sizeof(v)); memset(dfn,0,sizeof(dfn)); build(wmt); int r=1,l=1; for (int a=1;a<=(n<<1);a++) { int op,p=z[a].p; if (p<=n) op=pos[p+n]; else op=pos[p-n]; while (r<=a && z[r].v <= z[a].v-k) r++; if (r<a && r>=1 && z[r].v > z[a].v-k) { if (op>=r && op<=a-1) { if (op>r) insert(wmt,r,op-1,z[a].p); if (op<a-1) insert(wmt,op+1,a-1,z[a].p); } else insert(wmt,r,a-1,z[a].p); } while (l<=(n<<1) && z[l].v < z[a].v+k) l++; l--; if (l>a && l<=(n<<1) && z[l].v < z[a].v+k) { if (op>=a+1 && op<=l) { if (op>a+1) insert(wmt,a+1,op-1,z[a].p); if (op<l) insert(wmt,op+1,l,z[a].p); } else insert(wmt,a+1,l,z[a].p); } } for (int a=1;a<=(n<<1);a++) if (!dfn[a]) dfs(a); for (int a=1;a<=n;a++) if (belong[a]==belong[a+n]) return false; return true; } int main() { readint(n); int minv=0x3f3f3f3f,maxv=-0x3f3f3f3f; int x=0; for (int a=1;a<=n;a++) { int v1,v2; readint(v1); readint(v2); z[++x]=rec(v1,a); z[++x]=rec(v2,a+n); minv=min(minv,min(v1,v2)); maxv=max(maxv,max(v1,v2)); } if (maxv-minv+1 < n) { printf("0\n"); return 0; } sort(z+1,z+x+1); for (int a=1;a<=(n<<1);a++) pos[z[a].p]=a; int l=0,r=1000000001; while (l+1!=r) { int m=(l+r)>>1; if (check(m)) l=m; else r=m; } printf("%d\n",l); return 0; }
思考题:
N 1,2,3,4...n
有m组边 第i组边是从i向li~ri连一个长度为di的边
求1~n的最短路