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: