2017-2018 ACM-ICPC Pacific Northwest Regional Contest (Div. 1)
Time:2018.4.14 8:00-13:00
A sovled by ym&czh
题意
ym:签到,写复杂了,偶数回文串必定存在相邻相同,故可直接判断
B
题意
有n个歌手参加比赛,经过最后一轮的比赛后,他们的分数分别为pi;现在裁判有x分的附加分数, 每个歌手可以得到大于一分的附加分,裁判为了使比赛尽可能的充满激情,他们会每次选择一个选手,给这个选手加上qi的附加分并使他的分数为当前第一名,裁判每轮给的附加分不会比上一轮给的附加分低,每次宣布完一个歌手的分数后,最高得分的人一定是唯一的且最高得分的人要更新。宣布歌手的分数是按照歌手的附加分从小到大的顺序宣布,而宣布的是歌手的总分,按附加分从小到大宣布第ii个歌手总分的时候,后面的歌手的分数处于暂时不添加附加分数的状态
分析
C solved by czh&ym
题意
定义F(n):n的因子和,给出a,b,求(1 ≤ a ≤ b ≤ 1e12,b− a ≤ 1e6 ).
分析
czh : 枚举约数1到√b 的贡献,同时用等差数列求和的方法,求出大于√b一侧的贡献,当时知道另一侧会有贡献,但是没想到另一侧是等差数列,思考不够深入
D solved by ym
题意
给一颗n个节点的树,n-1条边,每条边都被染色,定义彩虹路径为:路径上不存在相邻的边是同一种颜色,定义一条边为good:到达这个点的所有路径都为彩虹路径,问那些点是good点,输出编号
分析
ym:经典树上做标记题,通过画成树形图观察不难发现,相邻的边的颜色相同
按照父节点是否相同可以分为两类
显然父节点相同的情况是:这两个儿子以及子树全部不符合
父节点不同(即一个点是另一个点的爷爷(父亲的父亲) ): 这种情况显然除了连接这两个点的点的子树(除去颜色相同的点),其余的点全部不符合
那么问题转化为:树上的一个节点如何打标记,使得我们可以找到符合条件的点,即一个节点的子树有哪些点,显然可以想到从一个节点递归的时候,一个节点访问的就时它的子树
所以我们可以任意选取一点为根,按照dfs序为记录每个节点的开始值(即该节点的值),和该点子树的范围(即子树节点个数)
那么现在我们所有问题全部解决,直接访问所有节点的边一次,差分前缀和打标记即可
#include<bits/stdc++.h> #define ll long long #define sc scanf #define pr printf #define rep(i,s,e) for(int i=s;i<=e;i++) using namespace std; const int maxn = 1e5+7; const int mod=1e9+7; int n; int head[maxn*2],nxt[maxn*2],to[maxn*2],c[maxn*2],tot; int st[maxn],ed[maxn],ans,f[maxn],orr[maxn*2], sum[maxn]; pair< int , int >g[maxn]; void add( int u, int v, int z) { c[++tot]=z; to[tot]=v; nxt[tot]=head[u]; head[u]=tot; } void dfs( int now, int fa) { f[now]=fa; st[now]=++ans; for ( int i=head[now];i;i=nxt[i]) { if (to[i]!=fa) dfs(to[i], now); } ed[now]=ans; } int main() { sc( "%d" ,&n); int u, v, val; rep(i,1,n-1) { sc( "%d%d%d" , &u, &v, &val); add(u,v,val), add(v,u,val); } dfs(1,0); for ( int i=1;i<=n;i++) { int t=0; for ( int j=head[i];j;j=nxt[j]) g[++t]=make_pair(c[j],to[j]); sort(g+1,g+t+1); int k; for ( int j=1;j<=t;j=k) { for (k=j;k<=t&&g[j].first==g[k].first;k++); if (k>j+1) { for ( int w=j;w<k;w++) { int y=g[w].second; if (y==f[i]) { sum[1]++; sum[st[i]]--; sum[ed[i]+1]++; } else { sum[st[y]]++; sum[ed[y]+1]--; } } } } } int answer=0; for ( int i=1;i<=n;i++) { sum[i]+=sum[i-1]; if (!sum[i]) answer++; } printf ( "%d\n" , answer); for ( int i=1;i<=n;i++) if (!sum[st[i]]) pr( "%d\n" , i); return 0; } |
E solved by czh
题意
物理题,czh秒了
F
几何,留坑
G make up by ym &czh
题意
给出n个房间和m扇门,共有k个人,每扇门可以从Ai到Bi,并且编号为Ci~Di的人都可以通过,给出S、T,问有多少人可以从S到T
(2 ≤ n ≤ 1,000; 1 ≤ m ≤ 5,000; 1 ≤ k ≤ 1e9 ) ( 1 ≤ s, t ≤ n; s != t) (1 ≤ ai , bi ≤ n; 1 ≤ ci ≤ di ≤ k; ai != bi)
分析
ym:将区间离散化,最多只有2m个数,考虑每次枚举一个数,则对于所有门的下界一定不会小于当前数比第一小的数,对O(m)进行暴力判断即可,时间复杂度O(m(n+m))
Trick:由于每次检查的区间为[ q[i-1]+1,q[i]],假设q[i-1]是某个区间的左端点,那么必然是要减1的 , 反之q[i-1]是某个区间的右端点,不管q[i-1]是否合法,我们都不应该修改
#include<cstdio> #include<algorithm> using namespace std; const int N=10010; int n,m,k,S,T,i,j,cnt,q[N],K; int g[N],ed,v[N],vc[N],vd[N],nxt[N],vis[N],ans; inline void add( int x, int y, int c, int d){ v[++ed]=y; vc[ed]=c; vd[ed]=d; nxt[ed]=g[x]; g[x]=ed; } void dfs( int x){ if (vis[x]) return ; vis[x]=1; for ( int i=g[x];i;i=nxt[i]) if (vc[i]<=K&&K<=vd[i])dfs(v[i]); } int main(){ scanf ( "%d%d%d%d%d" ,&n,&m,&k,&S,&T); for (i=1;i<=m;i++){ int a,b,c,d; scanf ( "%d%d%d%d" ,&a,&b,&c,&d); add(a,b,c,d); q[++cnt]=c-1; q[++cnt]=d; } sort(q+1,q+cnt+1); for (i=2;i<=cnt;i++) if (q[i]!=q[i-1]){ K=q[i]; for (j=1;j<=n;j++)vis[j]=0; dfs(S); if (vis[T])ans+=q[i]-q[i-1]; } printf ( "%d" ,ans); } |
H
题意
分析
dp+斜率优化+维护凸壳,留坑
I
题意
分析
大模拟,留坑
J solve by czh&ym
题意
给一个有n×m个格子的长方形,每个格子可以填B/R,若某个格子是B,则它的左上方全为B,现给出n,m,和一部分已经填充好的,问所有方案数(n,m <= 30)
分析
ym:赛时:辣鸡ym,定义dp[i][j][0/1]:表示格子(i,j)是B/R的状态数,状态有问题,因为这样定义后面的状态显然会影响前面的状态,违背了“无后效性”(一个B前面的所有状态应该都为0)
赛后:dp[i][j]:表示第i行前j个位B和方案数,注意状态不要重复
#include<bits/stdc++.h> #define ll long long #define sc scanf #define pr printf using namespace std; const int maxn = 1e5+7; const int mod=1e9+7; ll dp[35][35]; char a[35][35]; int ok[35][35]; int n,m; int init( int x, int y) { for ( int i=1;i<=y;i++) if (a[x][i]== 'R' ) return 0; for ( int i=y+1;i<=m;i++) if (a[x][i]== 'B' ) return 0; return 1; } int main() { sc( "%d%d" ,&n,&m); for ( int i=1;i<=n;i++) sc( "%s" , a[i]+1); for ( int i=0;i<=m;i++) dp[1][i]=init(1,i); ll ans=0; if (n==1) { for ( int i=0;i<=m;i++) { dp[1][i]=init(1,i); ans+=dp[1][1]; } printf ( "%I64d\n" , ans); return 0; } for ( int i=2;i<=n;i++) { for ( int j=0;j<=m;j++) { for ( int k=j;k<=m;k++) { dp[i][j]+=dp[i-1][k]*init(i,j); } if (i==n) ans+=dp[n][j]; //cout<<i<<' '<<j<<' '<<dp[i][j]<<endl; } } printf ( "%I64d\n" , ans); return 0; } |
K
题意
分析
ym:dp,待补
L solved by ym
题意
签到
Summary
Ym:辣鸡ym卡题,学弟上去直接A掉,比赛时卡题,没有迅速换题换人,造成时间上失败,认真听队友建议
Czh:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步