还会有02啊
糟心
T1 老司机的压缩包
老司机的压缩包
zip.cpp/.c/.pas
题目描述:
老司机最近得到了一个奇怪的压缩包,听说里面有十分厉害的东西呢!
但是这个压缩包有一个奇怪的密码设定;
首先有这个压缩包有一个数字n;
每次压缩包会给出一个数字[0,n-1]区间的整数,要求老司机回答这个数在mod n下的逆元是多少,如果逆元不存在则视为0即可;
老司机觉得这太简单了,但是因为n很大,老司机并不愿意一个一个输入进去;
所以老司机决定将[0,n-1]所有逆元的和输进去,这样也许就直接可以打开了呢;
然而老司机并不会做这个问题,所以就交给你了!
如果你的回答让老司机满意的话,他也许也会给你一个压缩包哦;
输入(zip.in):
第一行一个整数T,表示有T组测试数据;
然后T行,每行有一个正整数n;
输出(zip.out):
对于每组测试数据输出一行ans;
样例输入
1
4
样例输出
4
样例解释:
0,2在mod 4下不存在逆元;
1在mod 4下逆元为1;
3在mod 4下逆元为3;
数据范围:
20%的数据满足T<=10,1<n<10000且n为质数;
50%的数据满足T<=10,1<=n<=10000;
100%的数据满足T<=100,1<=n<=109
两个性质
性质1:集合{[0,n-1]中存在逆元的数}==集合{[0,n-1]中存在逆元的数的逆元}
证明:因为对逆元再求逆元得到的是本身,它们就是一一对应的了
这样问题就转化成了求[0,n-1]中与n互质的数的和
性质2:[0,n-1]中与互质的数的和为
证明:因为gcd(n,i)=gcd(n,n-i);
所以若 i 与 n 互质,那么 n-i 与 n 互质
和f(i)=f(n-i)相似(大概吧)
对称轴为x=(0+n)/2=n/2
所以与n互质的数可以关于n/2对称的,也就是相加等于n;
每两个质数对答案贡献为n/2*2=n
而现在有个质数
所以答案为
复杂度为求欧拉的复杂度,O( 根号n);
代码
1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 using namespace std; 5 int T; 6 long long n; 7 long long phi(long long x) 8 { 9 long long ans=x; 10 for(long long i=2;i*i<=x;i++) 11 { 12 if(x%i==0) 13 { 14 ans/=i; 15 ans*=i-1; 16 while(x%i==0) x/=i; 17 } 18 } 19 if(x!=1) ans/=x,ans*=x-1; 20 return ans; 21 } 22 int main() 23 { 24 freopen("zip.in","r",stdin); 25 freopen("zip.out","w",stdout); 26 scanf("%d",&T); 27 while(T--) 28 { 29 scanf("%lld",&n); 30 printf("%lld\n",n*phi(n)/2); 31 } 32 return 0; 33 }
T2 老司机的彩虹桥
老司机的彩虹桥
rainbow.cpp/.c/.pas
题目描述:
自从老司机有了好多好多小司姬之后,老司机就造了一个好大好大的房子;
因为老司机非常的6,这个房子不在地上而在天上!
我们可以将这个房子抽象成n片云朵和n-1条彩虹,每一条彩虹上都住着一个小司姬,当然了,所有云朵是由这些彩虹连通的树哦;
现在老司机想去探望所有老司机的小司姬,但是麻烦的是,他并不能进到小司姬的房间里——也就是不能通过彩虹桥来移动;
所以每次他只能到一个云朵上,然后探望和那个云朵相连的彩虹桥上的小司姬;
老司机想知道,他最少要去多少个云朵上才能探望所有的小司姬;
老司机还想知道,有多少个云朵是在所有的最优方案中他都不会上去的;
老司机还想知道,他究竟有多少种最优方案来探望小司姬们呢?
如果你的回答让老司机满意的话,他也许会邀请你去看小司姬哦;
输入(rainbow.in):
第一行一个整数n,代表有n个云朵;
然后n-1行,每行有两个整数x,y;
表示编号为x与y的云朵之间有一个彩虹桥;
输出(rainbow.out):
输出有三行,每行有一个数字;
第一行:老司机最少到多少个云朵上去探望小司姬;
第二行:有多少个云朵老司机在最优方案中不会上去;
第三行:老司机有多少种最优方案来探望小司姬们;
注意到第三问的方案数可能很大,所以取模5462617输出,前两问不取模
样例输入
5
1 2
1 3
2 4
2 5
样例输出
2
2
2
数据范围:
30%的数据满足n<=1000;
100%的数据满足n<=100000;
评分标准:
本题分三个问题给分;
第一问每个测试点4分,第二问每个测试点3分,第三问每个测试点3分;
相应位置的数字正确会获得相应得分;
如果输出不足或超过三个数字则不给分;
树形DP题
第一问
设 f [ x ] [ 0 ]表示 x 的子树满足条件并且不选 x 的最小代价;
设 f [ x ] [ 1 ]表示 x 的子树满足条件并且选 x 的最小代价;
最后答案就是 min ( f [ 1 ] [ 0 ],f [ 1 ] [ 1 ] )
第二问
设 p [ x ] [ 0 ]表示 x 点不选,除了 x 子树的所有部分的最小代价;
设 p [ x ] [ 1 ]表示 x 点选,除了 x 子树的所有部分的最小代价;
转移为:
当他自己不选,那么父节点必须选,此时整张图的代价为p [ fa[x] ] [ 1 ]+ f [ fa[x] ] [ 1 ] ,x的子树的贡献为 min ( f [ x ] [ 0 ] , f [ x ] [ 1 ] )
p [ x ] [ 0 ] = p [ fa[x] ] [ 1 ]+ f [ fa[x] ] [ 1 ] - min ( f [ x ] [ 0 ] , f [ x ] [ 1 ] )
若他自己选,那么父节点可选可不选
p [ x ] [ 1 ] = min ( p [ x ] [ 0 ] , p [ fa[x] ] [ 0 ] + f [ fa[x] ] [ 0 ] - f [ x ] [ 1 ] )
选了 x 点的最小答案就是 p [ x ] [ 1 ] + f [ x ] [ 1 ]
若这个答案大于第一问的结果,则 x 点永远不能选
第三问
设 t [ x ] [ 0 ]表示 x 的子树满足条件并且不选 x 的最小代价时的方案数;
设 t [ x ] [ 1 ]表示 x 的子树满足条件并且选 x 的最小代价时的方案数;
只是将第一问的累加变成了累乘
注意如果 f [ x ] [ 0 ] == f [ x ] [ 1 ] 也就是儿子两个都可以选的时候应该乘( t [ x ][ 1 ] + t [ x ][ 0 ] )
代码
1 #include<cstdio> 2 #include<iostream> 3 #define mod 5462617 4 using namespace std; 5 int ans,res,n,f[100010][2],k[100010][2],p[100010][2]; 6 int head[100010]; 7 //int to[100010],nxt[100010],cnt; 8 //int t[100010][2]; 9 int to[200010],nxt[200010],cnt; 10 long long t[100010][2]; 11 bool vis[100010]; 12 void add(int a,int b) 13 { 14 cnt++; 15 nxt[cnt]=head[a]; 16 head[a]=cnt; 17 to[cnt]=b; 18 } 19 int fa[100010]; 20 void dfs(int x) 21 { 22 f[x][1]=1; 23 t[x][1]=t[x][0]=1; 24 for(int i=head[x];i;i=nxt[i]) 25 { 26 if(to[i]==fa[x]) continue; 27 fa[to[i]]=x; 28 dfs(to[i]); 29 f[x][0]+=f[to[i]][1]; 30 t[x][0]*=t[to[i]][1]; 31 t[x][0]%=mod; 32 if(f[to[i]][1]<f[to[i]][0]) 33 { 34 f[x][1]+=f[to[i]][1]; 35 t[x][1]*=t[to[i]][1]; 36 t[x][1]%=mod; 37 } 38 if(f[to[i]][1]==f[to[i]][0]) 39 { 40 f[x][1]+=f[to[i]][1]; 41 t[x][1]*=(t[to[i]][1]+t[to[i]][0])%mod; 42 t[x][1]%=mod; 43 } 44 if(f[to[i]][1]>f[to[i]][0]) 45 { 46 f[x][1]+=f[to[i]][0]; 47 t[x][1]*=t[to[i]][0]; 48 t[x][1]%=mod; 49 } 50 } 51 } 52 void qiu(int x) 53 { 54 if(x>1) 55 { 56 //if(vis[fa[x]]==0) p[x][0]=ans-min(f[x][1],f[x][0]); 57 //p[x][1]=p[fa[x]][0]+f[fa[x]][0]-f[x][1]; 58 p[x][0]=p[fa[x]][1]+f[fa[x]][1]-min(f[x][1],f[x][0]); 59 p[x][1]=min(p[x][0],p[fa[x]][0]+f[fa[x]][0]-f[x][1]); 60 } 61 if(p[x][1]+f[x][1]!=ans) vis[x]=1,res++; 62 for(int i=head[x];i;i=nxt[i]) 63 { 64 if(to[i]==fa[x]) continue; 65 qiu(to[i]); 66 } 67 } 68 int main() 69 { 70 freopen("rainbow.in","r",stdin); 71 freopen("rainbow.out","w",stdout); 72 scanf("%d",&n); 73 for(int i=1,a,b;i<n;i++) 74 { 75 scanf("%d%d",&a,&b); 76 add(a,b); 77 add(b,a); 78 } 79 dfs(1); 80 ans=min(f[1][1],f[1][0]); 81 qiu(1); 82 if(f[1][1]>f[1][0]) 83 { 84 printf("%d\n%d\n%lld\n",f[1][0],res,t[1][0]); 85 } 86 if(f[1][1]==f[1][0]) 87 { 88 printf("%d\n",f[1][1]); 89 printf("%d\n",res); 90 printf("%lld\n",(t[1][0]+t[1][1])%mod); 91 } 92 if(f[1][1]<f[1][0]) 93 { 94 printf("%d\n%d\n%lld\n",f[1][1],res,t[1][1]); 95 } 96 return 0; 97 }
T3 小司姬的序列
小司姬的序列
girls.cpp/.c/.pas
题目描述:
自从老司机有了好多好多小司姬之后,老司机经常与她们一起玩游戏;
老司机总是喜欢吧小司姬们排成一列又一列的样子,然后向她们提出一些奇怪的问题,当然了,作为一个绅士,他是不会在询问开始之后再排列小司姬们的;
在序列中的每个小司姬都有一个可爱度ai,两个可爱度相同的小司姬视为相同的小司姬;
这一次,老司机提出的问题是这样的,对于两个小司姬序列x,y,它们相同的最长的小司姬序列有多长呢?(即查询两个序列的最长公共前缀LCP)
小司姬们当然会这个问题了,但是因为小司姬们实在是太可爱了,她们就又不会这个问题了,所以小司姬们向你求助,要你来解决这个问题;
如果你的回答让小司姬们满意的话,她们也许会邀请你一起来排队哦;
输入(girls.in):
第一行两个整数n,m,代表有n个序列,m次询问;
然后n行,每行为一个序列:
第一个整数len表示序列长度,然后len个整数为这个序列;
然后m行,每行两个整数x,y,表示老司机询问第x与第y个序列的LCP;
输出(girls.out):
输出m行,每行为对老司机询问的答案;
样例输入
3 3
3 1 2 3
3 1 2 4
5 1 2 3 4 5
1 3
2 1
2 3
样例输出
3
2
2
数据范围:
50%的数据满足n<=1000,m<=1000,序列总长度<=100000;
其中30%的数据满足序列元素大小1<=ai<=26;
100%的数据满足n<=100000,m<=100000,序列总长度<=500000;
序列元素大小0<=ai<=109;
字符集较大=1e9
用哈希+二分需要对出现的字符离散化
好难……
写一下Trie树吧
发现空间开不下
应用一个Trie的黑科技,长得像链式前向星
然后用map来存边
之后
LCA就是两字符串的LCP了
代码
1 #include<cstdio> 2 #include<map> 3 /* 4 map 5 插入: p.insert(pair<int,string>(1, "student_one")); 6 */ 7 using namespace std; 8 int n,m,root,a[500010]; 9 int ed[100010]; 10 int fa[500010][35],dep[500010],tot; 11 map<int,int> nxt[100010]; 12 int insert(int len) 13 { 14 int x=root; 15 for(int i=1;i<=len;i++) 16 { 17 map<int,int>::iterator iter=nxt[x].find(a[i]); 18 if(iter==nxt[x].end()) 19 { 20 nxt[x].insert(pair<int,int>(a[i],++tot)); 21 iter=nxt[x].find(a[i]); 22 fa[tot][0]=x; 23 } 24 x=iter->second; 25 } 26 return x; 27 } 28 void dfs(int x,int deep) 29 { 30 dep[x]=deep; 31 for(map<int,int>::iterator i=nxt[x].begin();i!=nxt[x].end();i++) 32 { 33 dfs(i->second,deep+1); 34 } 35 } 36 int lca(int x,int y) 37 { 38 if(dep[x]<dep[y]) swap(x,y); 39 for(int i=30;i>=0;i--) 40 { 41 if(dep[x]==dep[y]) break; 42 if(dep[fa[x][i]]>=dep[y]) x=fa[x][i]; 43 } 44 if(x==y) return x; 45 for(int i=30;i>=0;i--) 46 { 47 if(fa[x][0]==fa[y][0]) break; 48 if(fa[x][i]!=fa[y][i]) 49 { 50 x=fa[x][i]; 51 y=fa[y][i]; 52 } 53 } 54 return fa[x][0]; 55 } 56 int main() 57 { 58 freopen("girls.in","r",stdin); 59 freopen("girls.out","w",stdout); 60 scanf("%d%d",&n,&m); 61 for(int i=1,len;i<=n;i++) 62 { 63 scanf("%d",&len); 64 for(int j=1;j<=len;j++) 65 { 66 scanf("%d",&a[j]); 67 } 68 ed[i]=insert(len); 69 } 70 dfs(root,1); 71 for(int i=1;i<=30;i++) 72 { 73 for(int j=1;j<=tot;j++) 74 { 75 fa[j][i]=fa[fa[j][i-1]][i-1]; 76 } 77 } 78 for(int i=1,a,b;i<=m;i++) 79 { 80 scanf("%d%d",&a,&b); 81 printf("%d\n",dep[lca(ed[a],ed[b])]-1); 82 } 83 return 0; 84 }