ICPC PACIFIC NORTHWEST REGION 原题链接
A Radio Prize
题意:
一颗树,每个点点权ti,边权wi。输出n行,每行代表第x个点,它到所有点的 两点权和*路径 之和
\[ ans_x=\sum_{v=1}^{n} (t_x+t_v)d_{x,v}\]
数据范围
1<=k<=n<=200000
题解:
树形dp/树分治。想了两种转移方式
planA(简单):
\(ans_x\)由两部分构成,根节点 * 路径和+子节点 * 对应路径的和
设该树的根节点为 \(x,\) \[
\begin{array}{l}
a_{x}=\sum_{v=1}^{n} d_{x, v} \\
b_{x}=\sum_{v=1}^{n} t_{v} d_{x, v}
\end{array}\\
ans_x=t_xa_x+b_x
\] 结点y与x相邻,设以y为根节点的子树大小为\(tot_y\),子树点权值和为\(sum_y\),总点数为n,总点权和为SUM,则从x转到子节点y的方程为:
\[\begin{array}{l}
a_{y}=a_{x}+w_{xy}(n-2tot_y) \\
b_{x}=b_{x}+w_{xy}(SUM-2sum_y)
\end{array}\\\]
转移解释:
从x到y,橘色边w造成的影响有,黑色部分的点路径都减去w,红色部分的点路径都加上w
 tree_divide_and_conquer
tree_divide_and_conquer
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 
 | #include<bits/stdc++.h>using namespace std;
 int const maxn=1e5+10;
 int tot,head[maxn],n,m,val[maxn],tson[maxn],SUM;
 long long aa[maxn],bb[maxn],sum[maxn],ans[maxn];
 struct edge{
 int v,nxt;
 long long w;
 }e[maxn<<1];
 void build(int x,int y,int z){
 e[++tot].v=y;
 e[tot].nxt=head[x];
 e[tot].w=z;
 head[x]=tot;
 }
 void TreeSplitDfs1(int x,int f){
 tson[x]=1;
 sum[x]=val[x];
 for(int i=head[x];i;i=e[i].nxt){
 int v=e[i].v;
 if(v==f)continue;
 TreeSplitDfs1(v,x);
 tson[x]+=tson[v];
 sum[x]+=sum[v];
 aa[1]+=e[i].w*tson[v];
 bb[1]+=e[i].w*sum[v];
 }
 }
 void TreeSplitDfs2(int x,int f){
 for(int i=head[x];i;i=e[i].nxt){
 int v=e[i].v;
 if(v==f)continue;
 aa[v]=aa[x]+e[i].w*(n-2*tson[v]);
 bb[v]=bb[x]+e[i].w*(SUM-2*sum[v]);
 ans[v]=val[v]*aa[v]+bb[v];
 TreeSplitDfs2(v,x);
 }
 }
 int main(){
 
 scanf("%d",&n);
 tot=0;
 memset(head,0,sizeof(int)*(n+5));
 int a,b,c;
 for(int i=1;i<=n;i++){
 scanf("%d",&val[i]);
 SUM += val[i];
 }
 for(int i=1;i<n;i++){
 scanf("%d%d%d",&a,&b,&c);
 build(a,b,c);
 build(b,a,c);
 }
 TreeSplitDfs1(1,0);
 ans[1]=val[1]*aa[1]+bb[1];
 TreeSplitDfs2(1,0);
 for(int i=1;i<=n;i++)printf("%lld\n",ans[i]);
 
 
 return 0;
 }
 
 | 
planB(复杂):
\(ans_x\)由两部分构成 : 所有边 * 子树结点和 + 边 * 子节点个数 * 根节点权。算了转移太难写了,维护的东西还麻烦。。。不写了
B Perfect Flush
题意:给出n个数的序列,数字范围为1~k,找k个数都包含的最小子序列,保证有解。
数据范围
1<=k<=n<=200000
题解:贪心。我们肯定希望越小的元素放在前面越好,大的元素如果还有就把它尽量往后放,用栈来维护。
如果如果一个数在栈内,不管这个数。一个栈如果当前数小于栈顶,栈顶元素在后面还有的话,就把栈顶弹出,重复到不再弹栈,把这个数压进去。
结果为栈中的序列。
注意:在栈中的元素不要再用它来做弹栈操作了。因为它出现在前面更优。如下图:
 pop stock
pop stock
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 
 | #include <iostream>#include <stack>
 using namespace std;
 int const maxn=200005;
 int n,k,ans[maxn],tot,cou[maxn],a[maxn],g[maxn];
 stack<int>s;
 int main(){
 scanf("%d%d",&n,&k);
 for(int i=1;i<=n;i++){
 scanf("%d",&a[i]);
 cou[a[i]]++;
 }
 tot=0;
 for(int i=1;i<=n;i++){
 if(g[a[i]]>0)continue;
 if(!s.empty()){
 int u=s.top();
 while(cou[u]>0&&u>=a[i]){
 s.pop();
 g[u]--;
 if(s.empty())break;
 u=s.top();
 }
 }
 s.push(a[i]);
 g[a[i]]++;
 cou[a[i]]--;
 }
 for(int i=k;i>0;i--){
 ans[i]=s.top();
 s.pop();
 }
 for(int i=1;i<k;i++){
 printf("%d ",ans[i]);
 }
 printf("%d\n",ans[k]);
 return 0;
 }
 
 
 
 
 | 
C Coloring Contention
题意:
对图的边染色,使从1到n的路径中颜色变化最少的路径颜色变化最多。(最大化最小值)
数据范围:
2<=n<=1e5 1 <= m <= 1e5
题解:
bfs 染色 ,交替染色,当前点的出点中没染色的染为相反颜色,边跟出点同色。
答案:顺便用bfs找最短路,如果最短路为k,那最大变k-1次。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 
 | #include<bits/stdc++.h>using namespace std;
 int const maxn=1e5+10;
 int tot,head[maxn],n,m,val[maxn],dis[maxn];
 bool vis[maxn];
 struct edge{
 int v,nxt;
 }e[maxn<<1];
 void build(int x,int y){
 e[++tot].v=y;
 e[tot].nxt=head[x];
 head[x]=tot;
 }
 queue<int>q;
 int main(){
 
 scanf("%d%d",&n,&m);
 tot=0;
 memset(head,0,sizeof(int)*(n+5));
 int a,b;
 for(int i=1;i<=m;i++){
 scanf("%d%d",&a,&b);
 build(a,b);
 build(b,a);
 }
 q.push(1);
 int x;
 vis[1]=1;
 dis[1]=0;
 while(!q.empty()){
 x=q.front();q.pop();
 for(int i=head[x];i;i=e[i].nxt){
 int v=e[i].v;
 if(vis[v])continue;
 vis[v]=1;
 q.push(v);
 dis[v]=dis[x]+1;
 if(v==n)break;
 }
 }
 printf("%d\n",dis[n]-1);
 return 0;
 }
 
 | 
D Dividing by Two
题意:
问从A变到B的最小操作数,可以执行两个操作: 1.把A/2,只有A是奇数时可以 2.把A+1
数据范围:
 \[ 1 \leq a,b  \leq10^{9}\]
题解:
暴力。a<b输出b-a,a>b就一直除或加变到小于
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | #include<bits/stdc++.h>using namespace std;
 int const maxn=1e5+10;
 long long a,b;
 int main(){
 scanf("%lld%lld",&a,&b);
 if(a<b)printf("%lld\n",b-a);
 else{
 long long ans=0;
 while(a>b){
 if(a%2==0)a/=2;
 else a++;
 ans++;
 }
 printf("%lld\n",ans+(b-a));
 }
 return 0;
 
 }
 
 | 
E Rainbow Strings
题意:
给出字符串s,求所有字母不相同的子序列数量(空字符串也算) 答案取模11 092 019.
数据范围:
 \[ 1 \leq len(s)  \leq10^{5}\]
题解:
记录每个字母的数量num[i], \[ans= \prod_{i=a}^{z} num[i]+1\]
反正每个字母最多选一次
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | #include<bits/stdc++.h>using namespace std;
 int const maxn=1e5+10,mod=11092019;
 char s[maxn];
 long long  cou[600];
 int main(){
 scanf("%s",s);
 int len=strlen(s);
 for(int i=0;i<len;i++){
 cou[(int)s[i]]++;
 }
 long long ans=1;
 for(int i='a';i<='z';i++)ans=(ans*(cou[i]+1))%mod;
 printf("%lld\n",ans);
 
 return 0;
 
 }
 
 | 
F Carny Magician
求n个数的排列,满足:第k小的,有m个数在原位(pi=i)
G Glow, Little Pixel, Glow
??qwq没太看明白
H Pivoting Points
??qwq??qwq
I Error Correction
题意:
n个字符串(字符串中没有重复的字母,隐含字符串最长26),找到一个最大子集,使其中两两字符串x,y不能通过交换1对字母(不需要相邻)成为对方
输出最大子集大小
数据范围:
 \[ 1 \leq N  \leq 500\]
题解:
二分图最大独立集。
预处理:哪些字符串可以相互到达能暴力对比求出,把能相互转化到的字符串连一条边,问题变为找一般图的最大独立集。或者:把不能到达的字符串之间连边,问题变为找一般图的最大完全子图。
独立集:任意两点之间没有连边。
完全图:任意两点之间右边。
一般图的独立集或完全图时间复杂度过高,转化为二分图。
J Interstellar Travel
给定Ti ,si ,ai最大化
\[\sum_{1}^{n}\max \left(0, T_{i}-s_{i} \cdot \operatorname{dist}\left(a_{i}, a\right)\right) \]
dist(ai,a) 是ai到a最小转动的弧度
K Computer Cache
有n个字节的内存初始为0,下标从1到n
有m条数据 xi
询问q次三种操作之一:
- 1 i p 把第i条数据放到内存p 到p+len(xi)-1位置,保证操作有效不会溢出
- 2 p 询问内存p位置是什么数
- i l r 把xi中l到r位置数字都加1,对256取模
L Carry Cam Failure
定义一种新运算 加法没有进位,乘法运算中也无进位 给一个N,求最小的a满足a⊗a=N ,如果没有输出-1
M Maze Connect
??qwq