训练联盟训练赛之 ICPC Greater New York Region 2019
这场比赛感觉,难度区分不是很明显qwq(可能是我太菜了吧)
很快签完4个题,然后写完第五个题就开始挂机写作业去了。
C实际上不难,但是比赛的时候确实没有思路,不知道怎么处理近似这种无限传递的问题。
重点讲一下C和J吧。J其实我是通过打表来做的。
先贴四个签到题的代码:
A.FYI
给定一个7位数,判断前三个数字是不是555.
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f bool ispow(ll n){return (n&(n-1))==0;} #define lowbit(x) (x&(-x)) const int mod=1e9+7; int main() { fast; string s; cin>>s; if(s.substr(0,3)=="555") cout<<"YES\n"; else cout<<"NO\n"; return 0; }
G • Simple Collatz Sequence
给定一个这样的函数,再给定x,问你多少次操作后能把x变成1.
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f bool ispow(ll n){return (n&(n-1))==0;} #define lowbit(x) (x&(-x)) const int mod=1e9+7; int main() { fast; ll n; cin>>n; int cnt=0; while(n!=1){ if(n%2==1) n+=1; else n/=2; cnt++; } cout<<cnt<<'\n'; return 0; }
H • Problematic Public Keys
给定 n个整数,每个整数都是由两个质数相乘得到的。找出这些质数,一行五个输出。直接模拟就行。
被卡了两发PE()很难受
先把每个数分解成两个质数,然后用unique去重就好
具体做法是:
ans.erase(unique(ans.begin(),ans.end()),ans.end());
这里的ans是你定义的vector。下面是完整代码:
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f bool ispow(ll n){return (n&(n-1))==0;} #define lowbit(x) (x&(-x)) const int mod=1e9+7; vector<int>ans; bool isprime(int a) { for(int i=2;i<=sqrt(a);i++){ if(a%i==0) return false; } return true; } void getnum(int a){ for(int i=2;i<=sqrt(a);i++){ if(isprime(i)&&isprime(a/i)&&a%i==0){ ans.pb(i); ans.pb(a/i); break; } } } int main() { fast; int m; cin>>m; while(m--){ int n; cin>>n; getnum(n); } sort(ans.begin(),ans.end()); ans.erase(unique(ans.begin(),ans.end()),ans.end()); int len=ans.size(); int cnt=0; for(int i=0;i<len;i++){ cout<<ans[i]; cnt++; if(i==len-1) break; if(cnt%5==0) cout<<'\n'; else cout<<' '; } return 0; }
K • Regional Team Names
给你一个队名,要求队名必须由“-”来分隔,“-”前面长度在(1,8]之间,“-”后面长度在[1,24]之间。“-”后面再出现的“-”按照一个普通字符算,空格也看成一个字符。
直接处理就好。我还SB的WA了一发。
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f bool ispow(ll n){return (n&(n-1))==0;} #define lowbit(x) (x&(-x)) const int mod=1e9+7; int main() { fast; string s; getline(cin,s); int len=s.length(); int pos=0; for(int i=0;i<len;i++){ if(s[i]=='-') { pos=i; break; } } bool flag=true; if(pos<=1||pos>8) flag=false; if(len-pos<=1||len-pos>24) flag=false; if(!flag) cout<<"NO\n"; else cout<<"YES\n"; return 0; }
下面就到了我们关键的两道题了:
J • How Many Unicycles in a Wheel?
给你一个n个点的完全图。
现在把这张完全图改一改。先生成一个它的生成树,再添加一条边,要求图中只可以有一个环。外围的点还不能被绕开,我们定义这种环叫P环。
求把n个点的完全图改成P环的方案数。
这个题说实话,我是打表来做的。
我们从n=3开始看。
n=3的时候我们可以从6条边任意删除2条边。答案是C(6,3)= 15 种
n=4的时候,先从8条边里面任意删除3条边,但是发现,当删除一组邻边+邻边夹着的对角边是不符合条件的删法。
那么答案是C(8,3)- 4 = 52 种
n=5,样例给出来,170种
n=6的时候,先从12条边里面任意删除5条边。这种情况是C(12,5)= 792 种
然后我们考虑不符合条件的删法。
我们设与中心点相连的边为A类边,否则就是B类边
那么我们就有六种组合需要考虑了,分别是(0,5),(1,4),...(5,0)。
显然(0,5) (5,0)是一定满足条件的。
(1,4)和(4,1)也是满足条件的。(拿1,4来举例,删外面4条边里面一条边导致还有两个环的话,一定会有外围的孤立点存在,这是不符合生成树定义的)
(3,2)其实也是不可以的(没找到反例。。具体我不知道怎么去证明)
(2,3)是可以构造出来的。我们结合这个图一起看
我们先任意在里面取2条,在外面取3条,取法种数是C(6,2)*C(6,3) = 300 种。
下面我们来计算(2,3)情况下可以满足条件的取法:
如果里面删两条邻边,外面只可以删对面另一侧的三条边。删两条邻边有6种删法(旋转):种类数6 * 1 = 6 种 (例如:删除了边7和8,外面只能删除3,4,5来满足条件了)
如果里面删两条对边,显然现在有四个环,那么在外面只需要把四个环破坏掉三个即可。种类数6 * 4 = 24 种(例如,删除了边7和10,外面可以在1,(2,3),4,(5,6)四组中选择三组删掉一条)
否则,例如删除了边7和9,仍然有四个环,那么需要在(1,6),(2,3),4,5四组中选择三组删掉一条,但这种情况你会发现,在之后的构造里面有重复的情况出现。所以种类是是6 * 4 / 2 = 12 种
所以n=6的时候总数是 792 - 300 + 6 + 24 + 12 = 534 种。
然后我们发现,对于n个点的完全图,种类数一定是n的倍数。我们还惊奇的发现:
15/3=5
52/4=13
170/5=34
534/6=89
这些好像是斐波那契数列里面的项
然后我们就瞎猫碰上死耗子一样得到了答案Ans(n)=n*F(2n-1)
那就很简单了:
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f bool ispow(ll n){return (n&(n-1))==0;} #define lowbit(x) (x&(-x)) const int mod=1e5+7; ll f[10000]; int main() { fast; int n; cin>>n; f[1]=1,f[2]=1; for(int i=3;i<8200;i++) f[i]=(f[i-1]+f[i-2])%mod; cout<<n*f[2*n-1]%mod<<'\n'; return 0; }
这里还有一个正解,来自南理工的官方题解:
对于一个这样的图,设外圈点数为n,Unicycles的数量为n*S(n)
S(n)为不同的图的数目,乘n是旋转后的结果
对S(n+1),相当于在外圈多加了一个点,
对于圈的形成,可分为三种情况:
1.只有中心点不在圈上
2.k个点在圈上,中心点也在圈上,k<=n 设M(i)为第二种情况下,有i个点不在圈上的图的数目
显然M(0)=1,M(1)=3
我们在圈的旁边添加一个新点
这个点有三种添加方式:连接中心点,连接圈上的点(外圈),连接圈以外的点
每种方式分别是M(i)种图 其中第二三种方式有重复,即存在这个点同时与两边相连的情况,即为M(i-1)
综上,M(i+1)=3*M(i)-M(i-1)。 S(n)=sum(1,M(0),…,M(n-2));
当然可以用矩阵快速幂去处理,不过暴力的做法也是可以的。
C • Pass the Buck
最后一题了。
给定n个人构成的一张图,x手里有一个球,他可以把球给和他相邻的人(也可以给他自己),给每个人的概率是等可能的。
当一个人把球给自己的时候,游戏结束,这个人获胜。
求从x出发,最终y获胜的概率是多少(保留五位小数)。
这里我当时没想到,但实际上这是数据结构课上就学过的东西。
设一个邻接矩阵G,那么G[i][j]就表示从i到j长度为1的路径数
那G2[i][j]就表示从i到j长度为2的路径数
以此类推,Gn[i][j]就是长度为n的路径数
那我们这一题就抽象一下,用G[i][j]表示从i到j的可能性,设i和x个人相连接,那么传给每个人(包括自己)的概率就是1 / (x+1) 。换句话说就是 G[i][j] = 1 / (degree(i) + 1).
自己直接传给自己实际上就是G0[i][j] 了。(当然,自己传给自己那一定有i=j的)
那最后的答案就是:
还要除以degree(j)+1的原因就是,最后j还要自己传给自己,概率是1/(degree(j)+1)
由于我们只需要保留5位小数
直接设无穷在1005就好 精度已经够了
由于都是矩阵乘法,建议使用矩阵快速幂,不然显然会超时。
这里直接定义了矩阵类,可以直接用这个模板。
代码里面 下标都-1了是因为,我们使用的时候下标是从0开始的,但是题目的输入是从1开始的,统一一下。
#include<bits/stdc++.h> using namespace std; #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) #define mem(x,y) memset(x,y,sizeof(x)) #define ll long long #define pii pair<int,int> #define pll pair<ll,ll> #define mp make_pair #define pb push_back #define db double #define inf 0x3f3f3f3f bool ispow(ll n){return (n&(n-1))==0;} #define lowbit(x) (x&(-x)) const int mod=1e9+7; int num[25]; class matrix{ public: matrix(int nn,int mm){ n=nn; m=mm; } ~matrix(){}; int n,m; double val[21][21]; void init(){ mem(val,0); } matrix operator *(const matrix b) const{ matrix c(n,b.m); c.init(); for(int i=0;i<n;i++) for(int j=0;j<m;j++) for(int k=0;k<b.m;k++) c.val[i][k]=c.val[i][k]+(val[i][j]*b.val[j][k]); return c; } matrix operator +(const matrix b) const{ matrix c(n,b.m); c.init(); for(int i=0;i<n;i++) for(int j=0;j<m;j++) c.val[i][j]=val[i][j]+b.val[i][j]; return c; } }; matrix qpow(matrix a,int n){ matrix res(a.n,a.m); res.init(); for(int i=0;i<a.n;i++) res.val[i][i]=1; while(n){ if(n&1) res=res*a; a=a*a; n/=2; } return res; } int main() { int n,p; scanf("%d %d",&n,&p); matrix g(n,n),ans(n,n); g.init(); ans.init(); for(int i=0;i<n;i++){ scanf("%d",&num[i]); for(int j=0;j<num[i];j++){ int r; scanf("%d",&r); g.val[i][r-1]=1.0/(num[i]+1); } } for(int i=0;i<=1005;i++) ans=ans+qpow(g,i); for(int i=0;i<p;i++){ int t,st,ed; scanf("%d %d %d",&t,&st,&ed); db anss=ans.val[st-1][ed-1]*(1.0/(num[ed-1]+1)); printf("%d %.5f\n",t,anss); } return 0; }
那题解就到这里了,据说I也是个可做的题,之后有时间会补的。
之后还打算补一补CF
早睡的flag又倒了(