【C++】信息学联赛模拟题1+解析+代码

    科技2022-08-21  122

    信息学联赛模拟题1+解析+代码

    一. 选手须知1 指引(guide)【题目背景】【题目描述】【输入格式】【输出格式】【样例 1 输入】【样例 1 输出】【样例 1 解释】【样例 2】【样例 3】【样例 4】【样例 5】【数据范围及子任务】【解析】【代码】 2 碎片(fragment)【题目背景】【题目描述】【输入格式】【输出格式】【样例 1 输入】【样例 1 输出】【样例 1 解释】【样例 2】【样例 3】【数据范围及子任务】【解析】【代码】 3 寻梦(fantasy)【题目背景】【题目描述】【输入格式】【输出格式】【样例 1 输入】【样例 1 输出】【样例 1 解释】【样例 2】【样例 3】【样例 4】【样例 5】【数据范围及子任务】【解析】【代码】

    一. 选手须知

    题目名称指引碎片寻梦题目类型传统型传统型传统型可执行文件名guidefragmentfantasy输入文件名guide.infragment.infantasy.in输出文件名guide.outfragment.outfantasy.out每个测试点时限0.5 秒0.5 秒5.0 秒内存限制512MB512MB512MB测试点数目202014测试点是否等分是是否

    提交的源程序文件名

    对于 C++语言guide.cppfragment.cppfantasy.cpp对于 C 语言guide.cfragment.cfantasy.c对于 Pascal 语言guide.pasfragment.pasfantasy.pas

    编译选项

    对于 C++语言-O2 –lm-O2 –lm-O2 –lm对于 C 语言-O2 –lm -O2 –lm-O2 –lm对于 Pascal 语言-O2-O2-O2

    1 指引(guide)

    【题目背景】

    Wake up, dead boy, enter adventureland. Tricksters, magicians will show you all that’s real.

    【题目描述】

    N 名迷途的旅者需要小 X 的指引。 初始时, 每一名旅者 i 位于坐标(Ai,Bi)处, 旅者们只能够向右或是向上移动, 也就是说, 他们只能够增加自己的某一维坐标, 而不能减小它们。 这片大地上同样存在者 N 个出口, 每一个出口 i 位于坐标(Ci,Di)处, 一个出 口一旦被某个旅者通过, 它们就会一并消失。 请帮助小 X 计算他至多能够指引多少旅者离开这片大地。

    【输入格式】

    从文件 guide.in 中读取数据。 第一行一个整数 Num, 表示测试点编号, 以便选手方便地获得部分分, 你可 能不需要用到这则信息, 样例中 Num 的含义为数据范围与某个测试点相同。 接下来一行一个整数 N, 含义见题目描述。 接下来 N 行, 每行两个整数 Ai、 Bi, 表示每一名旅者的坐标。 接下来 N 行, 每行两个整数 Ci、 Di, 表示每一个出口的坐标。

    【输出格式】

    一行一个整数, 表示答案。

    【样例 1 输入】

    6 3 2 0 3 1 1 3 4 2 0 4 5 5

    【样例 1 输出】

    2

    【样例 1 解释】

    让位于(2,0)的旅者走到(4,2)处, 让位于(3,1)的旅者走到(5,5)处。

    【样例 2】

    见下发文件 guide2.in, guide2.ans

    【样例 3】

    见下发文件 guide3.in, guide3.ans

    【样例 4】

    见下发文件 guide4.in, guide4.ans

    【样例 5】

    见下发文件 guide5.in, guide5.ans

    【数据范围及子任务】

    对于所有测试数据, 保证 1≤N≤105, 0≤Ai、 Bi、 Ci、 Di<2N。 保证 A1,A2,…,AN,C1,C2,…,CN 两两不同。 保证 B1,B2,…,BN,D1,D2,…,DN 两两不同。 特殊性质 1: 保证 Ai=Bi。 特殊性质 2: 保证 Ci=Di。

    【解析】

    每个旅者可以选的出口就是从他自己的坐标到右上边界的一个矩形。

    同时考虑x,y坐标很难下手。我们将旅者和出口的坐标按x坐标排序。

    然后从大到小枚举,每个旅者匹配比其y坐标大的第一个出口(y坐标更大的出口可能匹配上一个y坐标大的旅者),可以使用线段树维护。

    【代码】

    #pragma GCC optimize(3,"Ofast","inline") #include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <set> using namespace std; typedef long long LL; const int N=2e5+5; set <int> st; bool type[N]; int pos[N]; template <typename T> void read(T &x) { x=0; int f=1; char c=getchar(); for(; !isdigit(c); c=getchar()) if(c=='-') f=-f; for(; isdigit(c); c=getchar()) x=x*10+c-'0'; x*=f; } template <typename T> void write(T x) { if(x<0) x=-x,putchar('-'); if(x>9) write(x/10); putchar(x%10+'0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int main() { freopen("guide.in","r",stdin); freopen("guide.out","w",stdout); int num; read(num); int n; read(n); st.clear(); for(int i=1; i<=n; i++) { int x,y; read(x),read(y); type[x]=1,pos[x]=y; } for(int i=1; i<=n; i++) { int x,y; read(x),read(y); type[x]=0,pos[x]=y; } int ans=0; for(int i=0; i<2*n; i++) { if(type[i]) st.insert(pos[i]); else { set <int>::iterator tmp=st.lower_bound(pos[i]); if(tmp==st.begin()) continue; tmp--; ans++; st.erase(tmp); } } writeln(ans); return 0; }

    2 碎片(fragment)

    【题目背景】

    In the ocean, deep down, Under raging waves, wrapped in memories, You’ll find wrecks of stately ships, They all went astray.

    【题目描述】

    小 X 的记忆中, 有一幅 N * M 的中心对称的图案。 而现在, 他的脑海中只有一幅有所变动的图案, 它同样是 N * M 的, 但却不 一定是中心对称的。 小 X 决定修补这一图案, 他能够进行的操作共有两种: 交换两行, 或者交换 两列。 小 X 想要知道能否通过有限(可能为 0) 次操作让它变得中心对称。 为了保证数据的强度, 一个测试点中可能包含多组测试数据。

    【输入格式】

    从文件 fragment.in 中读取数据。 第一行两个整数 Num、 T, Num 表示测试点编号, 以便选手方便地获得部分 分, 你可能不需要用到这则信息, 样例中 Num 的含义为数据范围与某个测试点 相同; T 表示该测试点中包含的测试数据的组数。 对于接下来每一组测试数据, 第一行两个整数 N、 M, 表示图案的大小。 接下来一个 N 行 M 列的字符矩阵, 表示图案。

    【输出格式】

    输出 T 行, 每行一个 YES 或 NO, 表示是否能够使图案中心对称。

    【样例 1 输入】

    6 1 2 3 ABC BAC

    【样例 1 输出】

    YES

    【样例 1 解释】

    交换第 2 列和第 3 列, 得到以下图案: ACB BCA 它是中心对称的。

    【样例 2】

    见下发文件 fragment2.in, fragment2.ans

    【样例 3】

    见下发文件 fragment3.in, fragment3.ans

    【数据范围及子任务】

    对于所有测试数据, 保证 1≤N、 M≤12, 1≤T≤10, 图案由大写字母组成。 特殊限制: 图案仅由 A 和 B 组成。

    【解析】

    这些行和列的位置并不重要,只需要考虑相对应的行和列使得合法即可,那么暴力选择第一个未被选择的行,再去找一个匹配,时间复杂度 O ( ( 12 ! / 6 ! / 2 6 ) 2 t ) = O ( 1 0 8 t ) O((12!/6!/2^6)^2 t) = O(10^8t) O((12!/6!/26)2t)=O(108t) (然后如果是奇数行或奇数列,需要枚举某一行/列在最中间)

    考虑剪枝,很明显两行/列要匹配至少每一种字母个数要相同,因此可以预处理出可以匹配的两个行/列(可能也不需要剪枝),然后再匹配一组后判断当前能否合法。

    【代码】

    #pragma GCC optimize(3,"Ofast","inline") #include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> using namespace std; typedef long long LL; const int N=15; int n,m; int rnum[N],cnum[N]; bool solved; bool visrow[N],viscol[N]; bool row[N][N],col[N][N]; char mp[N][N]; template <typename T> void read(T &x) { x=0; int f=1; char c=getchar(); for(; !isdigit(c); c=getchar()) if(c=='-') f=-f; for(; isdigit(c); c=getchar()) x=x*10+c-'0'; x*=f; } template <typename T> void write(T x) { if(x<0) x=-x,putchar('-'); if(x>9) write(x/10); putchar(x%10+'0'); } template <typename T> void writeln(T x) { write(x); puts(""); } inline void check() { for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) if(mp[rnum[i]][cnum[j]]!=mp[rnum[n-i+1]][cnum[m-j+1]]) return; printf("YES\n"); solved=1; } inline void workc(int pos,int type) { if(solved) return; if(pos == 0) { check(); return; } if(type) { for(int i=1; i<=m; i++) { viscol[i]=1; cnum[pos]=i; workc(pos-1,0); viscol[i]=0; } } else { for(int i=1; i<=m; i++) { if(viscol[i]) continue; for(int j=i+1; j<=m; j++) if(!viscol[j] && col[i][j]) { viscol[i]=1; viscol[j]=1; cnum[pos] = i; cnum[m+1-pos]=j; workc(pos-1,0); viscol[i]=0; viscol[j]=0; } return; } } } void workr(int pos,int type) { if(solved) return; if(pos==0) { workc((m+1)/2,m&1); return; } if(type) { for(int i=1; i<=n; i++) { visrow[i]=1; rnum[pos]=i; workr(pos-1,0); visrow[i]=0; } } else { for(int i=1; i<=n; i++) { if(visrow[i]) continue; for(int j=i+1; j<=n; j++) if(!visrow[j] && row[i][j]) { visrow[i]=1; visrow[j]=1; rnum[pos]=i; rnum[n+1-pos]=j; workr(pos-1,0); visrow[i]=0; visrow[j]=0; } return; } } } int main() { freopen("fragment.in","r",stdin); freopen("fragment.out","w",stdout); int T,num; read(num),read(T); while (T--) { read(n),read(m); for(int i=1; i<=n; i++) scanf("%s",mp[i]+1); for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) { static char x[N], y[N]; for(int k=1; k<=m; k++) { x[k]=mp[i][k]; y[k]=mp[j][k]; } sort(x+1,x+m+1); sort(y+1,y+m+1); row[i][j]=1; for(int k=1; k<=m; k++) if(x[k]!=y[k]) row[i][j]=0; } for(int i=1; i<=m; i++) for(int j=1; j<=m; j++) { static char x[N], y[N]; for(int k=1; k<=n; k++) { x[k]=mp[k][i]; y[k]=mp[k][j]; } sort(x+1,x+n+1); sort(y+1,y+n+1); col[i][j]=1; for(int k=1; k<=n; k++) if(x[k]!=y[k]) col[i][j]=0; } solved=0; workr((n+1)/2,n&1); if(!solved) printf("NO\n"); } return 0; }

    3 寻梦(fantasy)

    【题目背景】

    A nightingale in a golden cage, That’s me locked inside reality’s maze. Come someone make my heavy heart light, Come undone, bring me back to life ?

    【题目描述】

    N 名旅者背井离乡, 前往他乡寻梦。 初始时, 旅者 i 位于自己的家乡 i 号城市。 每一天, 位于 i 号城市的所有旅者都会前往 Ai 号城市, 其中 Ai 是一个在整 个过程中保持不变的量, 由于旅者迫切的寻梦欲望, 我们规定 Ai≠ i。 现在小 X 想要知道, 是否存在至少一组满足条件的 Ai, 使得在 K 天后, 所有 旅者会同时重新回到家乡(在这过程中旅者们同样可以途径自己的家乡) 。 为了保证数据的强度, 一个测试点中可能包含多组测试数据。

    【输入格式】

    从文件 fantasy.in 中读取数据。 第一行两个整数 Num、 T, Num 表示测试点编号, 以便选手方便地获得部分 分, 你可能不需要用到这则信息, 样例中 Num 的含义为数据范围与某个测试点 相同; T 表示该测试点中包含的测试数据的组数。 接下来 T 行, 每一行两个整数 N、 K, 描述每一组数据。

    【输出格式】

    输出 T 行, 每行一个 YES 或 NO, 表示是否存在至少一组满足条件的 Ai。

    【样例 1 输入】

    2 3 7 7 3 8 5 6

    【样例 1 输出】

    YES NO YES

    【样例 1 解释】

    对于第一组测试数据, 可以令 A={2,3,4,5,6,7,1}。 对于第二组测试数据, 可以证明不存在符合条件的 A。 对于第三组测试数据, 可以令 A={3,4,1,5,2}。

    【样例 2】

    见下发文件 fantasy2.in, fantasy2.ans

    【样例 3】

    见下发文件 fantasy3.in, fantasy3.ans

    【样例 4】

    见下发文件 fantasy4.in, fantasy4.ans

    【样例 5】

    见下发文件 fantasy5.in, fantasy5.ans

    【数据范围及子任务】

    对于所有测试数据, 保证 1 ≤ T ≤ 1 0 4 , 1 ≤ N ≤ 1 0 18 , 1 ≤ K ≤ 1 0 1 5 1≤T≤10^4, 1≤N≤10^{18}, 1≤K≤10^15 1T1041N10181K1015。 保证一个测试点中不同的 K 的个数至多为 50。

    【解析】

    题意即求是否有一组非负整数解 x i x_i xi ∑ x i ∗ p i = n \sum x_i*p_i=n xipi=n( p i p_i pi为k的所有质因子) 首先对其质因数分解,对质因子个数分类讨论:

    1个,即判断 p 1 p_1 p1是否整除n即可;2个,扩欧求出一组解,然后调整将其中一个变为最小的非负数,判断乘积是否小于等于n即可;多于2个,显然最小的质因子不超过105,记作 p 0 p_0 p0,同时如果 n = x n = x n=x可行,则 n = x + k p 0 n=x+kp_0 n=x+kp0也可行,因此只需要找到 ∀ 0 ≤ T < p 0 {\forall}0 \le T< p_0 0T<p0,找到最小的 n = a ( m o d   p 0 ) n = a(mod\ p_0) n=a(mod p0) 使得n可行.

    具体的,可以看作最短路,将 ( i , ( i + p j ) m o d p 0 ) ( j > 0 ) (i,(i+p_j)mod p_0) (j>0) (i,(i+pj)modp0)(j>0)连边,之后求出0到x的最短路即为最小的n,用dij算法即可。

    【代码】

    该代码要256MB的内存限制

    #pragma GCC optimize(3,"Ofast","inline") #include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <queue> using namespace std; typedef long long LL; const int Q=55; const int LOG=64; const int N=1e5+5; const int P=2e6+5; const int V=3.2e7+5; const LL INF=4e18; template <typename T> void read(T &x) { x=0; int f=1; char c=getchar(); for(; !isdigit(c); c=getchar()) if(c=='-') f=-f; for(; isdigit(c); c=getchar()) x=x*10+c-'0'; x*=f; } template <typename T> void write(T x) { if(x<0) x=-x,putchar('-'); if(x>9) write(x/10); putchar(x%10+'0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int q,tot; int prime[Q],f[V],cnt[Q]; LL memk[Q]; LL p[Q][LOG],dist[Q][N]; struct info { long long dist; int home; }; bool operator < (info a, info b) { return a.dist > b.dist; } void exgcd(LL a,LL b,LL &x,LL &y) { if(b==0) { x=1; y=0; return; } LL q=a/b,r=a%b; exgcd(b,r,y,x); y-=q*x; } int main() { freopen("fantasy.in","r",stdin); freopen("fantasy.out","w",stdout); int num; read(num); for (int i=2; i<V; i++) { if(f[i]==0) prime[++tot]=f[i]=i; for (int j=1; j<=tot && prime[j]<=f[i]; j++) { int tmp=prime[j]*i; if(tmp>=V) break; f[tmp]=prime[j]; } } int T; read(T); while(T--) { LL n, k; read(n),read(k); int pos=0; for(int i=1; i<=q; i++) if(memk[i]==k) pos=i; if(pos==0) { pos=++q; memk[q]=k; LL tmp=k; for(int i=1; 1LL*prime[i]*prime[i]<=tmp; i++) if(tmp%prime[i]==0) { p[pos][++cnt[pos]]=prime[i]; while(tmp%prime[i]==0) tmp/=prime[i]; } if(tmp!=1) p[pos][++cnt[pos]]=tmp; if(cnt[pos]>=3) { for(int i=0; i<p[pos][1]; i++) dist[pos][i]=INF; static priority_queue <info> Heap; dist[pos][0]=0; Heap.push((info){0,0}); static bool vis[N]; memset(vis,0,sizeof(vis)); while(!Heap.empty()) { while(!Heap.empty() && vis[Heap.top().home]) Heap.pop(); if(Heap.empty()) break; info tmp=Heap.top(); Heap.pop(); for(int i=2; i<=cnt[pos]; i++) { int dest=(tmp.home+p[pos][i])%p[pos][1]; if (dist[pos][dest]>tmp.dist+p[pos][i]) { dist[pos][dest]=tmp.dist+p[pos][i]; Heap.push((info){dist[pos][dest],dest}); } } } } } bool flg=0; for(int i=1; i<=cnt[pos]; i++) if(n%p[pos][i]==0) { printf("YES\n"); flg=1; break; } if(flg) continue; if(cnt[pos]<=1) { printf("NO\n"); continue; } if(cnt[pos]==2) { LL x=0,y=0; exgcd(p[pos][1],p[pos][2],x,y); y=(y%p[pos][1]+p[pos][1])%p[pos][1]; LL tmp=y*(n%p[pos][1])%p[pos][1]*p[pos][2]; if(tmp<=n) printf("YES\n"); else printf("NO\n"); continue; } int tmp=n%p[pos][1]; if(dist[pos][tmp]<=n) printf("YES\n"); else printf("NO\n"); } return 0; }
    Processed: 0.019, SQL: 10