ICPC 2019 徐州网络赛

比赛时间:2019.9.7

比赛链接:The Preliminary Contest for ICPC Asia Xuzhou 2019

赛后的经验总结

// 比赛完才反应过来没看完全部题, J题树形dp没写太亏了。。。
// 几何旋律AK了,太强了Orz
// 虽然我们队A了8题,但如果时间分配好,认真读题避免看错题的话,应该至少多A一题


A. Who is better?

题意

两个人玩游戏,谁先消灭完敌人就赢。两条规则:1. 第一个人不能全部消灭完; 2. 后一次消灭的人数不超过前一次的两倍。

敌人人数通过 n 个同余方程求出。

非常明显的斐波那契博弈 + 中国剩余定理。

 

坑点

很裸的两个知识点强行结合在一起,于是愉快地抄起板子来,一交即WA,再交继续WA...

考虑到 CRT 计算过程中 ll 会溢出,改写 __int128,继续WA...

最后想起洛谷上测板子时候,把合并过程中的 a[i] >= m[i] 条件注释掉,然后就A了。。。

也就是说,测试数据认为余数比模数大也是合法的。

 

AC代码(新的 CRT 加强模板)

#include<iostream>
#include<cstdio>
#include<set>
using namespace std; typedef long long ll;
typedef __int128 i128;
const ll maxn = 1e15; i128 exgcd(i128 a, i128 b, i128& x, i128& y) {
if(b==0) {
x = 1; y = 0;
return a;
}
i128 d = exgcd(b, a%b, y, x);
y -= (a/b) * x;
return d;
} i128 a[20], m[20];
int n; i128 excrt() {
if (n==1) {
if (m[0]>a[0]) return a[0];
else return -1;
}
i128 x, y, d;
for (int i=1;i<n;i++) {
d = exgcd(m[0], m[i], x, y);
if ((a[i]-a[0])%d!=0) return -1; i128 t = m[i] / d;
x = ((a[i]-a[0]) / d * x % t + t) % t;
a[0] = x * m[0] + a[0];
m[0] = m[0] * m[i] / d;
a[0] = (a[0]%m[0] + m[0]) % m[0];
}
return a[0];
} ll fib[100];
int main() {
cin>>n;
for(int i=0;i<n;i++) {
ll a1, a2;
cin>>a1>>a2;
m[i] = a1, a[i] = a2;
} i128 x = excrt();
if(x<=1 || x>maxn) {
return 0 * printf("Tankernb!\n");
} set<ll> S;
fib[1] = 1;
for(int i=2;i<100;i++) {
fib[i] = fib[i-1]+fib[i-2];
if(fib[i]>maxn) break;
S.insert(fib[i]);
}
if(S.find(x)==S.end())
printf("Zgxnb!\n");
else
printf("Lbnb!\n");
}

 

B. so easy

题意

有 n 个点从 1~n 编号,q 次操作 (z, x) 。(\(1 \le x < n <10^9\), \(1 \le q<10^6\))

z = 1 时,删除点 x;z = 2 时,查询从 x 之后能到达的第一个点(包括 x )。

 

思路

n 的范围太大,线段树什么都存不了。

卡了很长时间,一直段错误。最后队友Yu用对查询离散化+并查集A了。

 

题解代码(使用 unordered_map)

#include<iostream>
#include<cstdio>
#include<unordered_map>
using namespace std;
const int maxn = 100010;
unordered_map<int, int> fa; int findfa(int x) {
if(!fa.count(x)) return x;
return fa[x] = findfa(fa[x]);
} int main() {
int n, q;
scanf("%d %d", &n, &q);
int op, x;
while(q--) {
scanf("%d %d", &op, &x);
if(op==1) {
fa[x] = findfa(x+1);
} else {
int ans = findfa(x);
if(ans>n) ans = -1;
printf("%d\n", ans);
}
}
return 0;
}

 

C. Buy Watermelon

题意

出题人的sb英语,三个人读完题都不知道写的什么。签到题WA了三次给强行怼过了。

大概题目想表达的是,要把西瓜分成重量都为偶数克的两部分吧。

 

思路

西瓜重量 w 必须为大于2的偶数。

 

AC代码

略。

 

D. Carneginon

队友Wu写的,据说是简单的KMP,也是签到题。

AC代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10; char s[maxn],t[maxn];
int nexts[maxn],nextt[maxn]; void getnext_t(char *ptr){
int i,n,k;
n=strlen(ptr);
k=-1;
nextt[0]=-1;
i=0; while(i<n)
{
if(k==-1 || ptr[i]==ptr[k])
{
i++;
k++;
nextt[i]=k;
}
else k=nextt[k];
}
} void getnext_s(char *ptr){
int i,n,k;
n=strlen(ptr);
k=-1;
nexts[0]=-1;
i=0; while(i<n)
{
if(k==-1 || ptr[i]==ptr[k])
{
i++;
k++;
nexts[i]=k;
}
else k=nexts[k];
}
} int kmps(char *a,char *b)//匹配ab两串,a为父串
{
int i=0,j=0;
int len1=strlen(a);
int len2=strlen(b);
getnext_t(t);
while(i<len1&&j<len2)
{
if(j==-1||a[i]==b[j])
++i,++j;
else
j=nextt[j];
}
if(j==len2)
return i-j;
else
return -1;
} int kmpt(char *a,char *b)//匹配ab两串,t为父串
{
int i=0,j=0;
int len1=strlen(a);
int len2=strlen(b);
while(i<len1&&j<len2)
{
if(j==-1||a[i]==b[j])
++i,++j;
else
j=nexts[j];
}
if(j==len2)
return i-j;
else
return -1;
} bool cmp(char *a, char *b){
int len=strlen(s);
for(int i=0;i<len;i++) {
if(a[i]!=b[i]) return 0;
}return 1; } int main()
{
scanf("%s",s);
getnext_s(s);
int q;scanf("%d",&q);
for(int i=1;i<=q;i++){
scanf("%s",t);
int ls=strlen(s),lt=strlen(t);
if(ls==lt){
if(cmp(s,t)) printf("jntm!\n");
else printf("friend!\n");
}else if(ls>lt){
if(kmps(s,t)!=-1) printf("my child!\n");
else printf("oh, child!\n");
}else {
if(kmpt(t,s)!=-1) printf("my teacher!\n");
else printf("senior!\n");
}
}
return 0;
}

 

E. XKC's basketball team

题意

定义 w[i] 的 angry 值 为 w[i] 与右边最远的满足 w[j] >= w[i] + m 之间的人数。

求所有点的 angry 值之和。

 

思路

队友Yu写的,当时我沉浸在数论题中。。。

 

AC代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define inf 1000000000
using namespace std;
const int N = 5e5+10;
int tree[N<<4],num[N],cnt;
int ls[N<<4],rs[N<<4];
void update(int l,int r,int pos,int x,int d)
{
if(l==r)
{
tree[pos] = max(tree[pos],d);
return;
}
int mid = (l+r)>>1;
if(x<=mid)
{
if(ls[pos]==0)
ls[pos] = ++cnt;
update(l,mid,ls[pos],x,d);
}
else
{
if(rs[pos]==0)
rs[pos] = ++cnt;
update(mid+1,r,rs[pos],x,d);
}
tree[pos]=max(tree[ls[pos]],tree[rs[pos]]);
}
int qry(int l,int r,int L,int R,int pos)
{
if(L>R)return 0;
if(pos==0)return 0;
if(L<=l && r<=R)
return tree[pos];
int mid = (l+r)>>1;
int ret = 0;
if(L<=mid)
ret = max(ret,qry(l,mid,L,R,ls[pos]));
if(mid<R)
ret = max(ret,qry(mid+1,r,L,R,rs[pos]));
return ret;
}
int arr[N],hx[N];
int head;
int main()
{
cnt = 0;
int n,m;scanf("%d%d",&n,&m);
head = ++cnt;
for(int i=1;i<=n;++i)
{
scanf("%d",&arr[i]);
hx[i] = arr[i];
//update(1,inf,head,arr[i],i);
}
sort(hx+1,hx+1+n);
int len = unique(hx+1,hx+1+n)-hx-1;
for(int i=1;i<=n;++i)
{
int pos = lower_bound(hx+1,hx+1+len,arr[i])-hx;
update(1,len,head,pos,i);
}
bool ok = 1;
for(int i=1;i<=n;++i)
{
if(ok==0)
printf(" ");
ok=0;
int rs = arr[i]+m;
if(rs>hx[len])
printf("-1");
else
{
int pos = lower_bound(hx+1,hx+1+len,rs)-hx;
//cout<<endl<<i<<" "<<rs<<" "<<hx[pos]<<" "<<pos<<endl;
int ans = qry(1,len,pos,len,head);
if(ans<=i)
printf("-1");
else
printf("%d",ans-i-1);
}
}
puts("");
return 0;
}

 

G. Colorful String

题意

给定一个字符串,定义 value 为回文子串的不同字母个数,求它的全部回文子串的 value 之和。

 

思路

应该是字符串水题。

我用马拉车调了半天没弄出来。字符串队友那边用回文自动机直接一发A了。

 

AC代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn=3e5+5;
const int N=26;
struct Palindromic_Tree
{
int next[maxn][N];
int fail[maxn];
int cnt[maxn];//第i个节点表示的回文串出现的次数,最后要调用count函数完成计算
int val[maxn];
int len[maxn];//节点i表示的回文串的长度
int S[maxn];//存放添加的字符
bool used[26]; int last;
int n;//字符数组指针
int p;//节点指针
int newnode(int l)
{
memset(next[p],0,sizeof next[p]);
cnt[p]=0;
len[p]=l;
return p++;
}
void init()
{
p=0;
newnode(0);
newnode(-1);
memset(used,0,sizeof used);
last=0;
n=0;
S[n]=-1;
fail[0]=1;
}
int get_fail(int x)
{
while(S[n-len[x]-1]!=S[n])
x=fail[x];
return x;
}
void add(int c)
{
c-='a';
S[++n]=c;
int cur=get_fail(last);
if(!next[cur][c])
{
int now=newnode(len[cur]+2);
fail[now]=next[get_fail(fail[cur])][c];
next[cur][c]=now;
}
last=next[cur][c];
cnt[last]++;
}
void count()
{
for(int i=p-1;i>=0;i--)
cnt[fail[i]]+=cnt[i];
}
void dfs(int now,ll num){
for(int i=0;i<26;i++) {
if(next[now][i]){
if(!used[i]) {
used[i]=1;
dfs(next[now][i],num+1);
used[i]=0;
}else dfs(next[now][i],num);
}
}
val[now]=num;
}
}pam;
char s[maxn]; int main()
{
scanf("%s",s);
pam.init();
int len=strlen(s);
for(int i=0;i<len;i++) pam.add(s[i]);
pam.count(); pam.dfs(0,0);
pam.dfs(1,0);
ll ans=0;
for(int i=2;i<pam.p;i++) ans+=1LL*pam.cnt[i]*pam.val[i];
printf("%lld\n",ans);
return 0;
}

 

J. Random Access Iterator (补)

题意

在树上按照图中 dfs 的方式,每次随机选一个儿子往下 dfs 得到树的深度。

求程序返回树的正确深度的概率。

 

思路

树形dp

比赛时候没有写,我的锅,全耗在数论题上了,结果全是通过数不超过100的难题。

注意题目程序的 dfs 每个节点跑了 k 次,相当于独立重复实验。

设tmp为节点 u 能达到根节点的概率, $tmp = \sum {1 \over k} dp[v] \(
\)dp[u] = 1 - (1-tmp)^k$

 

AC代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
const int mod = 1e9+7;
const int maxn = 1000100;
typedef long long ll; vector<int> G[maxn];
ll dp[maxn], ans;
int dep[maxn], maxDep; ll powmod(ll a, ll n) {
ll res = 1;
while(n) {
if(n&1) res = res * a % mod;
a = a * a % mod;
n >>= 1;
}
return res;
} void getDep(int u, int fa, int d) {
dep[u] = d;
maxDep = max(d, maxDep);
for(int i=0;i<G[u].size();i++) {
int v = G[u][i];
if(v!=fa) {
getDep(v, u, d+1);
}
}
} void dfs(int u, int fa) {
if(dp[u]!=-1) return; ll tmp = 0;
int son = G[u].size();
if(fa!=-1) --son; for(int i=0;i<G[u].size();i++) {
int v = G[u][i];
if(v==fa) continue; dfs(v, u);
tmp = (tmp + dp[v]*powmod(son, mod-2)%mod ) % mod;
}
dp[u] = (1-powmod((1-tmp+mod)%mod, son) + mod) % mod;
} int main() {
int n; scanf("%d", &n);
for(int i=0;i<n-1;i++) {
int u, v;
scanf("%d %d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
} getDep(1, -1, 1);
for(int i=1;i<=n;i++) {
if(dep[i]==maxDep)
dp[i] = 1;
else {
if(G[i].size()==1)
dp[i] = 0;
else
dp[i] = -1;
}
// cout<<dp[i]<<endl;
}
dp[1] = -1;
dfs(1, -1);
printf("%lld\n", dp[1]); return 0;
}

 

K. Center

题意

给定平面上 n 个点,求加入最少的点使全部点能够中心对称。

 

思路

时限2s,复杂度O(n^3*logn)的暴力枚举算法勉强能过。

直接写完交一发,果然T了。

改用unordered_map存两点映射后的整数,降低了 logn 的复杂度,还是T了。

发现固定中心点的时候,剩下的点的对称点不会发生重合,直接检查是否插入新的点,带上 if(now>ans) break; 优化check过程,最后A了,几分钟之和才拿下A题。

 

AC代码(赛后改回了set可以A)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<set>
#include<algorithm>
using namespace std;
const int maxn = 100010;
typedef pair<int, int> pii;
struct Point {
int x, y;
Point(int xx=0, int yy=0):x(xx), y(yy) {}
}p[1010]; set<pii> S; int main() {
int n; scanf("%d", &n);
for(int i=0;i<n;i++) {
scanf("%d %d", &p[i].x, &p[i].y);
S.insert(make_pair(p[i].x, p[i].y));
} int ans = n;
for(int i=0;i<n;i++) {
for(int j=i+1;j<n;j++) {
int now = 0;
for(int k=0;k<n;k++) {
if(S.find(make_pair(p[i].x+p[j].x-p[k].x, p[i].y+p[j].y-p[k].y))==S.end())
++now;
if(now>ans) break; // 没有会TLE
}
ans = min(ans, now);
}
} for(int i=0;i<n;i++) {
int now = 0;
for(int j=0;j<n;j++) {
if(i==j) continue;
if(S.find(make_pair(2*p[i].x-p[j].x, 2*p[i].y-p[j].y))==S.end())
++now;
if(now>ans) break;
}
ans = min(ans, now); }
printf("%d\n", ans);
return 0;
}

 

M. Longest subsequence

题意

给定两个字符串 s, t ,求 s 串中满足字典序大于 t 串的最长子序列长度。

 

思路

又是字符串,大概dp吧。

Wu读错题,让Yu白搞了快一个小时。。。

注意是子序列不是子串啊!!!

 

AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e6+10;
char str[N];
int dp[N][30];
char ptn[N];
int main()
{
int n,m;scanf("%d%d",&n,&m);
scanf("%s%s",str+1,ptn+1);
m++;
ptn[m]='a'-1;
for(int i=0;i<26;++i)
dp[n][i]=n*2;
for(int i=n-1;i>=0;i--)
{
for(int j=0;j<26;++j)
dp[i][j] = dp[i+1][j];
dp[i][str[i+1]-'a'] = i+1;
}
int ans = 0;
int now = 0;
for(int i=1;i<=m && now<=n;++i)
{
for(int j=ptn[i]-'a'+1;j<26;++j)
if(dp[now][j]<=n)
ans = max(ans,n-dp[now][j]+i);
now = dp[now][ptn[i]-'a'];
//cout<<i<<" "<<ans<<" "<<now<<endl;
}
if(ans)
cout<<ans<<endl;
else
puts("-1");
return 0;
}

 


(未完待补题。。。)

ICPC 2019 徐州网络赛的更多相关文章

  1. The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 K题 center

    You are given a point set with nn points on the 2D-plane, your task is to find the smallest number o ...

  2. The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 XKC's basketball team

    XKC , the captain of the basketball team , is directing a train of nn team members. He makes all mem ...

  3. The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 D Carneginon

    Carneginon was a chic bard. But when he was young, he was frivolous and had joined many gangs. Recen ...

  4. The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 C Buy Watermelon

    The hot summer came so quickly that Xiaoming and Xiaohong decided to buy a big and sweet watermelon. ...

  5. The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 B so easy

    题目链接:https://nanti.jisuanke.com/t/41384 这题暴力能过,我用的是并查集的思想,这个题的数据是为暴力设置的,所以暴力挺快的,但是当他转移的点多了之后,我觉得还是我这 ...

  6. The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 A Who is better?

    A After Asgard was destroyed, tanker brought his soldiers to earth, and at the same time took on the ...

  7. query 2019徐州网络赛(树状数组)

    query \[ Time Limit: 2000 ms \quad Memory Limit: 262144 kB \] 题意 补题才发现比赛的时候读了一个假题意.... 给出长度为 \(n\) 的 ...

  8. 2019徐州网络赛 I J M

    I. query 比赛时候没有预处理因子疯狂t,其实预处理出来因子是\(O(nlog(n))\)级别的 每个数和他的因子是一对偏序关系,因此询问转化为(l,r)区间每个数的因子在区间(l,r)的个数 ...

  9. 2019徐州网络赛 H.function

    题意: 先有\(n=p_1^{k_1}p_2^{k_2}\cdots p_m^{k_m}\),定义\(f(n)=k_1+k_2+\cdots+k_m\). 现在计算 \[ \sum_{i=1}^nf( ...

随机推荐

  1. redis数据库操作

    一.String(字符串)操作 String在redis中的存储是按照key-value的形式存储 1.SET key value [EX seconds] [PX milliseconds] [NX ...

  2. Laravel Route Resource 方法

    新增的 resource 方法将遵从 RESTful 架构为用户资源生成路由.该方法接收两个参数,第一个参数为资源名称,第二个参数为控制器名称. Route::resource('users', 'U ...

  3. leetcode-158周赛-5222-分割字符串

    题目描述: 自己的提交: class Solution: def balancedStringSplit(self, s: str) -> int: if not s:return 0 res ...

  4. HIVE常用SQL语句及语法

    HIVE建内部表语句 create table dll102.sougou (id string,pwd string,name string,count int,seqno int,address ...

  5. R语言 基本语法

    R语言基本语法 我们将开始学习R语言编程,首先编写一个"你好,世界! 的程序. 根据需要,您可以在R语言命令提示符处编程,也可以使用R语言脚本文件编写程序. 让我们逐个体验不同之处. 命令提 ...

  6. kafk的数据消费快速的原因

    kafka为什么消费数据很快呢? 1.数据的顺序读写 2.页缓存(操作系统层面) https://blog.csdn.net/gdj0001/article/details/80136364

  7. IOS自动化打包介绍

    IOS自动化打包介绍  标签: app打包 , Ios打包 , iphone打包 , iphone自动化打渠道包    分类:无线客户端技术, 贴吧技术 摘要 随着苹果手持设备用户的不断增加,ios应 ...

  8. JavaScript笔记 – 程序语法设计

    一.基础语法设计 JavaScript是可以与HTML标记语言混合.用于网页交互式的解释型脚本语言.由国际标准ECMAScript提供核心语言功能.文档对象模型(DOM)提供访问和操作网页内容的方法和 ...

  9. Qt无边框窗口的移动、拉伸边框、鼠标滚轮缩放大小

    主要是处理窗口上鼠标的几个事件,具体代码请看下面的截图, 完整代码的下载链接在此:http://download.csdn.net/detail/beyond0824/9657110, 本示例代码中, ...

  10. 《Java语言程序设计》编程练习6.18(检测密码)

    6.18 (检测密码)一些网站对于密码具有一些规则.编写一个方法,检测字符串是否是一个有效密码.     假定密码规则如下:     • 密码必须至少8位字符.     • 密码仅能包含字母和数字. ...