终于到了饭吉圆杯的开赛,这是EZ我参与的历史上第一场ACM赛制的题目然而没有罚时

不过题目很好,举办地也很成功,为法老点赞!!!

这次和翰爷,吴骏达 dalao,陈乐扬dalao组的队,因为我们有二个初二的,所以并起来算一个 。

然后我和另外一个初二的连键盘都没摸,靠着翰爷的大杀四方成功A了5题并因为罚时惜败得到Rank2。%%%Orz 翰爷%%%

好了下面开始讲题。饭吉圆链接

与一般的ACM相似,这次考试的题目也分为三档

  • Easy:NOIp普及组+难度(法老认为);NOIp提高组T1,T2难度(我认为)
  • Medium:比较套路略加一点思维的NOIp提高组题 (法老认为);NOIpT3+至弱省省选题(我认为)
  • Hard: 省选常见算法题,难度略低于省选(法老认为);ZJOI/HNOI省选题+NOI-神题(我认为)

好了我是真的菜,并且针对我无比菜的水平,我也只改了Easy+Medium。

Easy(难度递增)

I. 「QZEZ第一届“饭吉圆”杯程序设计竞赛」画展

整场比赛最水的题目了,模拟题不解释。

由于\(n\le 111\),因此连Hash都不用上。我们直接把图变成字符串然后开Map存一下即可

CODE

#include<iostream>
#include<cstdio>
#include<map>
#include<string>
using namespace std;
const int N=120;
map <string,bool> t;
int n,m,a,b,ans;
char g[N][N];
bool vis[N][N];
string s;
inline void find(void)
{
for (register int i=2;i<=n;++i)
if (g[i][2]=='#') { a=i-2; break; }
for (register int i=2;i<=m;++i)
if (g[2][i]=='#') { b=i-2; break; }
}
inline void solve(int x1,int y1,int x2,int y2)
{
register int i,j;
for (s="",i=x1;i<=x2;++i)
for (j=y1;j<=y2;++j)
s+=g[i][j],vis[i][j]=1;
if (t[s]) return;
for (s="",i=x2;i>=x1;--i)
for (j=y2;j>=y1;--j)
s+=g[i][j];
if (t[s]) return;
if (a==b)
{
for (s="",j=y2;j>=y1;--j)
for (i=x1;i<=x2;++i)
s+=g[i][j];
if (t[s]) return;
for (s="",j=y1;j<=y2;++j)
for (i=x2;i>=x1;--i)
s+=g[i][j];
if (t[s]) return;
}
t[s]=1; ++ans;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register int i,j; ios::sync_with_stdio(false); cin>>n>>m;
for (i=1;i<=n;++i)
for (j=1;j<=m;++j)
cin>>g[i][j]; find();
for (i=1;i<=n;++i)
for (j=1;j<=m;++j)
if (g[i][j]!='#'&&!vis[i][j]) solve(i,j,i+a-1,j+b-1);
return printf("%d",ans),0;
}

F. 「QZEZ第一届“饭吉圆”杯程序设计竞赛」数学作业

这道题有两种方法。

第一种当然是暴力证明了,由于我不会,因此给出法老的手写证明

第二种就比较策略了。题目中提到了:

\(f(s)\)的函数图像似乎是一个极其诡异的曲线

然后我们根据相似三角形感性理解一下其实面积变大的话其形状不会改变,只有边长会按比例增加。

因此我们可以结合样例得到:

\(f(s)=A \sqrt S=1.63299\sqrt S\)

然后写上去一交发现WA了!Why?精度!

我们来猥琐一波:

\((1.63299)^2=2.666...=\frac{8}{3}\)

所以\(A=\frac{2\sqrt S}{3}\)

然后就水过了。

CODE

#include<cstdio>
#include<cmath>
using namespace std;
int main()
{
int n; scanf("%d",&n);
printf("%.5lf",(double)sqrt(n*8.0/3.0));
return 0;
}

B. 「QZEZ第一届“饭吉圆”杯程序设计竞赛」最小完美生成树

也是比较简单的题目,要正确理解题意

首先我们先对原图跑一遍MST,如果求出来的MST已经包含多种颜色,那么直接输出答案即可。

如果只有一种颜色,我们就挑一条不同颜色的边,然后肯定会有一条边被替换下来。

我们找到这条边的最大值即可。

这种方法可以和求LCA一起搞,主要就是倍增。

令\(f_{i,j}\)表示第\(i\)条边向上\(2^j\)次步的路径上的最大值,然后和LCA一起做即可(因为刚好也查询到LCA)

具体维护看CODE

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100005,P=20;
struct edge
{
int to,next,v;
}e[N<<1];
struct data
{
int l,r,w,col;
bool use;
}a[N];
int head[N],n,m,pa[N][P],f[N][P],father[N],dep[N],cnt,c,rt=1;
long long tot;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch=tc();
while (ch<'0'||ch>'9') ch=tc();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline bool comp(data a,data b)
{
return a.w<b.w;
}
inline void add(int x,int y,int z)
{
e[++cnt].to=y; e[cnt].v=z; e[cnt].next=head[x]; head[x]=cnt;
}
inline int max(int a,int b)
{
return a>b?a:b;
}
inline int min(int a,int b)
{
return a<b?a:b;
}
inline int getfather(int k)
{
return father[k]==k?k:father[k]=getfather(father[k]);
}
inline void DFS(int now,int fa)
{
for (register int i=head[now];i!=-1;i=e[i].next)
if (e[i].to!=fa) f[e[i].to][0]=e[i].v,pa[e[i].to][0]=now,dep[e[i].to]=dep[now]+1,DFS(e[i].to,now);
}
inline void init(void)
{
for (register int j=0;j<P-1;++j)
for (register int i=1;i<=n;++i)
if (pa[i][j]) pa[i][j+1]=pa[pa[i][j]][j],f[i][j+1]=max(f[i][j],f[pa[i][j]][j]);
}
inline void swap(int &a,int &b)
{
int t=a; a=b; b=t;
}
inline int getmax(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y); register int i; int res=0;
for (i=P-1;i>=0;--i)
if (pa[x][i]&&dep[pa[x][i]]>=dep[y]) res=max(res,f[x][i]),x=pa[x][i];
if (x==y) return res;
for (i=P-1;i>=0;--i)
if (pa[x][i]&&pa[y][i]&&pa[x][i]!=pa[y][i])
{
res=max(res,max(f[x][i],f[y][i]));
x=pa[x][i]; y=pa[y][i];
}
return max(res,max(f[x][0],f[y][0]));
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register int i; read(n); read(m);
memset(e,-1,sizeof(e));
memset(head,-1,sizeof(head));
for (i=1;i<=m;++i)
read(a[i].l),read(a[i].r),read(a[i].w),read(a[i].col);
sort(a+1,a+m+1,comp);
for (i=1;i<=n;++i)
father[i]=i;
for (i=1;i<=m;++i)
{
int fx=getfather(a[i].l),fy=getfather(a[i].r);
if (fx!=fy)
{
father[fx]=fy; tot+=a[i].w; a[i].use=1;
add(a[i].l,a[i].r,a[i].w); add(a[i].r,a[i].l,a[i].w);
}
}
for (i=1;i<=m;++i)
if (a[i].use)
{
if (!c) { c=a[i].col; continue; }
if (c!=a[i].col) return printf("%lld",tot),0;
}
DFS(rt,-1); init(); long long ans=1e18;
for (i=1;i<=m;++i)
if (!a[i].use&&a[i].col!=c) ans=min(ans,tot-getmax(a[i].l,a[i].r)+a[i].w);
return printf("%lld",ans),0;
}

Medium(难度递增)

C. 「QZEZ第一届“饭吉圆”杯程序设计竞赛」路径计数

比较套路的数位DP

我们考虑对题意进行转换,考虑把走过的点写下来,那么一个点的二进制是另一个点的二进制的子集

所以每一个路径上的数肯定是一段1,然后一段0。

就相当于要确定二进制每一位的出现次数,然后出现的二进制位值之和要小于等于\(k\),总和等于\(n\)

仔细想一想,这就是个多重背包了(怎么好像就我一个人写多重背包)

CODE

#include<cstdio>
#include<cctype>
#include<cstring>
using namespace std;
const int N=1e5+5,mod=1e9+9;
int n,k,f[N],x;
inline void inc(int &x,int y)
{
if ((x+=y)>=mod) x-=mod;
}
inline void dec(int &x,int y)
{
if ((x-=y)<0) x+=mod;
}
int main()
{
register int i,j; scanf("%d%d",&k,&n); f[0]=1;
for (i=0;(x=1<<i)<=n;++i)
{
for (j=0;j<=n;++j)
if (j>=x) inc(f[j],f[j-x]);
for (j=n;j>=0;--j)
if (j>=(long long)x*(k+1)) dec(f[j],f[j-(long long)x*(k+1)]);
}
return printf("%d",f[n]),0;
}

K. 「QZEZ第一届“饭吉圆”杯程序设计竞赛」朋友圈

这是典型的技巧题,首先我们想一下这个问题的两种解决方法:

  1. 暴力扫描每一条边,判断两端是否在点集内。复杂度为\(O(qm)\)
  2. 枚举点集中的两个点,然后判断是否存在边 。复杂度为\(O(q\cdot s\ log\ m)\)

然后这两种算法的效率取决于每组数据的点数。我们设一个阈值\(S\):

当点数大于\(S\)时进行算法1,否则进行算法2。

然后我们可以得出取\(S=\frac{q}{log\ m}\)时复杂度达到理论最小值\(O(q\cdot\sqrt{m\ log\ m})\)

CODE

#include<cstdio>
#include<cctype>
#include<cmath>
#include<map>
#include<set>
using namespace std;
const int N=200005;
struct edge
{
int x,y;
}e[N];
map <int,bool> vis[N];
int n,m,q,blk,t,ans,num[N];
bool s[N];
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void write(int x)
{
if (x>9) write(x/10);
putchar(x%10+'0');
}
inline int solve1(void)
{
register int i,ans=0;
for (i=1;i<=m;++i)
if (s[e[i].x]&&s[e[i].y]) ++ans;
return ans;
}
inline int solve2(void)
{
register int i,j,ans=0;
for (i=1;i<t;++i)
for (j=i+1;j<=t;++j)
if (vis[num[i]][num[j]]) ++ans;
return ans;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register int i; read(n); read(m);
for (i=1;i<=m;++i)
read(e[i].x),read(e[i].y),vis[e[i].x][e[i].y]=vis[e[i].y][e[i].x]=1;
read(q); blk=q/(int)log2(m);
while (q--)
{
for (read(t),i=1;i<=t;++i)
read(num[i]),s[num[i]]=1;
write(t>blk?solve1():solve2()); putchar('\n');
for (i=1;i<=t;++i)
s[num[i]]=0;
}
return 0;
}

H. 「QZEZ第一届“饭吉圆”杯程序设计竞赛」字符串匹配2

这道题也是有点骚的,就着法老的std写的,第一次知道双Hash的正确写法

首先读题,我们考虑枚举所有的周期,然后对于每一段我们可以结合特殊最小表示法+双Hash来\(O(1)\)求

然后这里我们要知道一个调和级数公式(其实我是知道的,也和另外一个初二口头AC了这道题,不过最后时间不够了,而且我们也不敢上):

\(n+\frac{n}{2}+\frac{n}{3}+\frac{n}{4}+\cdots+\frac{n}{n-1}+1=n\ In\ n\)

然后把每种字符出现的位置用01二进制串表示,然后hash,判断是否能两两对应就可以了 。

CODE

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define fir first
#define sec second
using namespace std;
typedef pair<int,int> Hash;
const int N=1e5+5;
const Hash seed={233,2333},mod={1e9+7,1e9+9};
vector <Hash> a,b;
char s[N];
int len;
Hash pre[5][N],pw[N];
Hash operator %(Hash a,Hash b) { return Hash((a.fir+b.fir)%b.fir,(a.sec+b.sec)%b.sec); }
Hash operator +(Hash a,Hash b) { return Hash(a.fir+b.fir,a.sec+b.sec)%mod; }
Hash operator -(Hash a,Hash b) { return Hash(a.fir-b.fir,a.sec-b.sec)%mod; }
Hash operator *(Hash a,Hash b) { return Hash(1LL*a.fir*b.fir%mod.fir,1LL*a.sec*b.sec%mod.sec); }
inline void write(int x)
{
if (x>9) write(x/10);
putchar(x%10+'0');
}
inline int min(int a,int b)
{
return a<b?a:b;
}
inline Hash get_sub(int k,int l,int r)
{
return pre[k][r]-(pre[k][l-1]*pw[r-l+1]);
}
int main()
{
register int i,j,k; scanf("%s",s+1); len=strlen(s+1);
for (pw[0]={1,1},i=1;i<=len;++i)
pw[i]=pw[i-1]*seed;
for(i=1;i<=len;++i)
{
for(j=0;j<5;++j)
pre[j][i]=pre[j][i-1]*seed;
pre[s[i]-'a'][i]=pre[s[i]-'a'][i]+Hash(1,1);
}
for (i=1;i<=len;++i)
{
bool flag=1;
for (j=i+1;j<=len;j+=i)
{
a.clear(); b.clear();
for (k=0;k<5;++k)
a.push_back(get_sub(k,1,min(i,len-j+1))),b.push_back(get_sub(k,j,min(i+j-1,len)));
sort(a.begin(),a.end()); sort(b.begin(),b.end());
if (a!=b) { flag=0; break; }
}
if (flag) write(i),putchar(' ');
}
return 0;
}

E. 「QZEZ第一届“饭吉圆”杯程序设计竞赛」暑假作业

首先先发现对\(t\)从小到大排序后可以得到最优解(这个很好证明而且也很显然,实在不行结合样例理解一下即可)。

考虑排序后如何算出初始的最优解,我们可以分析后得出答案等价于

\(\sum_{i=1}^n l_i-t_i\cdot(n-i+1)\)

然后我们考虑修改,其实就是取走一个数在放上一个数的过程,我们发现只要得出这个点(老的)对答案的贡献和新的点对答案的贡献。

很显然,一个点被删除/加入对于它的排名有影响,对排在它后面的数的答案也有影响。

因此我们分别考虑这两个影响,排名可以用类似于求逆序对的方法用树状数组求知。

然后考虑对于排在它后面的数统统前移一位,其实也是一个求和(\(\sum t_i\))的过程,因此我们再开一维树状数组来直接统计即可。

具体实现看CODE

#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL N=2e5+5;
LL n,m,l[N],t[N],r[N],x,nl,nt,ans,tree[2][N>>1];
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(LL &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void write(LL x)
{
if (x<0) putchar('-'),x=-x;
if (x>9) write(x/10); putchar(x%10+'0');
}
inline void add(LL x,LL y,LL z)
{
for (;x<=1e5;tree[z][x]+=y,x+=x&-x);
}
inline LL get(LL x,LL y)
{
LL tot=0; for (;x;tot+=tree[y][x],x-=x&-x); return tot;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register LL i; read(n); read(m);
for (i=1;i<=n;++i)
read(l[i]),read(t[i]),r[i]=t[i],ans+=l[i];
sort(r+1,r+n+1);
for (i=1;i<=n;++i)
ans-=r[i]*(n-i+1),add(r[i],1,0),add(r[i],r[i],1);
write(ans); putchar('\n');
while (m--)
{
read(x); read(nl); read(nt);
ans-=l[x]; l[x]=nl; ans+=l[x];
add(t[x],-1,0); add(t[x],-t[x],1);
ans+=t[x]*(n-get(t[x],0))+get(t[x],1);
t[x]=nt; ans-=t[x]*(n-get(t[x],0))+get(t[x],1);
add(t[x],1,0); add(t[x],t[x],1);
write(ans); putchar('\n');
}
return 0;
}

QZEZ第一届“饭吉圆”杯程序设计竞赛的更多相关文章

  1. 华南理工大学“三七互娱杯”程序设计竞赛(重现赛)( HRY and array 高精度除法模板)

    题目链接:https://ac.nowcoder.com/acm/contest/874/D 题目大意:给你两个数列a和b然后对a可以进行排列,对b可以任意排列,问你sigma(a(i)*b(i))的 ...

  2. 华南理工大学“三七互娱杯”程序设计竞赛 HRY and codefire(概率期望DP)

    https://ac.nowcoder.com/acm/contest/874/A 题目:有两个账号 , 一开始都为0级 , 求任意一个账号升级到N的期望 要求:如果当前账号嬴了 , 就继续沿用当前的 ...

  3. 长安大学第四届ACM-ICPC“迎新杯”程序设计竞赛-重现赛 G - 彩虹岛套娃

    题目描述 俄罗斯套娃是俄罗斯特产的木制玩具,一般由多个一样图案的空心木娃娃一个套一个组成,最多可达十多个,通常为圆柱形,底部平坦可以直立.颜色有红色,蓝色,绿色,紫色等.最普通的图案是一个穿着俄罗斯民 ...

  4. 长安大学第四届ACM-ICPC“迎新杯”程序设计竞赛-重现赛 H - 圣诞节糖果

    题目描述 圣诞节临近,彩虹岛的黑心商人

  5. 长安大学第四届ACM-ICPC“迎新杯”程序设计竞赛-重现赛 F - 打铁的箱子

    题目描述 作为彩虹岛上最擅长打铁的人,

  6. 长安大学第四届ACM-ICPC“迎新杯”程序设计竞赛-重现赛 D - 新卡片游戏

    题目描述

  7. 长安大学第四届“迎新杯”程序设计竞赛 F 打铁的箱子【数学/进制思维/折半枚举】

    题目描述 作为彩虹岛上最擅长打铁的人,

  8. 长安大学第四届“迎新杯”程序设计竞赛 H 圣诞节糖果【二分】

    时间限制:C/C++ 1秒,其他语言2秒空间限制:C/C++ 131072K,其他语言262144K64bit IO Format: %lld 题目描述 圣诞节临近,彩虹岛的黑心商人

  9. 华南理工大学“三七互娱杯”程序设计竞赛 G: HRY and tree

    题意:给出一棵树,定义两点间代价为两点路径上最长的边权,问任两点间的代价和. 解法:这道题的解法十分巧妙:直接用Kruskal对这棵树求最小生成树,然后对于即将加入到MST的这条边(u,v,w),这条 ...

随机推荐

  1. js判断当前浏览器语言类型

    console.log(window.navigator.language.slice(0, 2)); 得到的是zh

  2. (网页)js常见报错之Unexpected token in JSON at position

    出现这个报错提示,根本原因只有一个--json解析异常,所以请大家直接去关注自己json的返回数据注意检查其返回内容和内容的格式是否正确,至于本文血案的导火索是因为json注释滴问题.

  3. (网页)Uncaught ReferenceError: pageImport is not defined

    在js开发中,很多人遇到类似问题,都找不到解决方法.Uncaught ReferenceError: $ is not defined,在这里给大家提供几个解决方法. 1.出现这个错误,最可能的是引用 ...

  4. (后端)shiro:Wildcard string cannot be null or empty. Make sure permission strings are properly formatted.

    访问某页面时,出现了这个异常: java.lang.IllegalArgumentException: Wildcard string cannot be null or empty. Make su ...

  5. JS笔记(一):基础知识

    (一) 标识符 标识符就是一个名字,在JS中,标识符用来对变量和函数命名,或者用做JS代码中某些循环语句中的跳转位置的标记.JS的标识符必须以字母._或$符号开始,后续字符可以是字母.数字._或$符号 ...

  6. python并发编程之线程

    操作系统线程理论 参考资料:http://www.cnblogs.com/Eva-J/articles/8306047.html 线程和python 理论知识 全局解释器锁GIL Python代码的执 ...

  7. java死锁示例及其发现方法

    在java多线程编程中很容易出现死锁,死锁就是多个线程相互之间永久性的等待对方释放锁,这和数据库多个会话之间的死锁类似.下面的代码示例了一个最简单的死锁的例子,线程1和线程2相互之间等待对方释放锁来取 ...

  8. 卷积运算的本质,以tensorflow中VALID卷积方式为例。

    卷积运算在数学上是做矩阵点积,这样可以调整每个像素上的BGR值或HSV值来形成不同的特征.从代码上看,每次卷积核扫描完一个通道是做了一次四重循环.下面以VALID卷积方式为例进行解释. 下面是pyth ...

  9. <20180927>新开一篇章记录常用到的IT名词

    (一)有些名词在市场化的今天用的很多,和IT的名词重叠和易混淆, 有些缩写根本连外国人都很少用, 国人却用的津津乐道.这篇是专门用来记录的. 1.1 SMB , 这个在IT领域用的很多, 阐述的是 “ ...

  10. Properties集合_修改配置信息

    集合中的数据来自于一个文件  注意:必须要保证该文件中的数据是键值对.  需要使用到读取流 使用load()方法读取已有文件中的数据,存储到Properties集合中 public class Pro ...