2018 Multi-University Training Contest 4
1001:Problem A. Integers Exhibition
重要结论:一个数如果是k-magic数,那么它的倍数也是k-magic数。 发现非k-magic数很少
一开始知道1是非k-magic数。从小到大枚举质数。
对于每个质数p,将已知的数乘以p的幂加入数列直到超出上限,然后筛掉全部k-magic数(按数字从小到大维护前k多因数数量)。
直到连续几个p没有改变数列停止。此时p约为293。
在k是233的数列中以同样的方法筛出k是0到233的234个数列。
二分答案
1002:Problem B. Harvest of Apples
能O(1)转移就能离线莫队啊当时没想到
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> #include <cmath> #include <vector> #include <iostream> #include <stack> #include <set> #include <map> #define LSON l,m,x<<1 #define RSON m+1,r,x<<1|1 using namespace std; const int MAX=1e5+5; const int mod=1e9+7; int t,n,m; int len; struct node{ int l,r,id; }q[MAX]; long long fac[MAX],inv[MAX],ans[MAX]; int cmp(node a,node b){ if(a.l/len==b.l/len) return a.r<b.r; return a.l/len<b.l/len; } long long qp(long long p,long long q){ long long cnt=1; while(q>0) { if(q%2==1) cnt=(cnt*p)%mod; p=(p*p)%mod; q/=2; } return cnt; } long long C(int a,int b){ long long tmp=fac[a]*inv[b]%mod*inv[a-b]%mod; return tmp; } void init(){ inv[0]=fac[0]=1; for(int i=1;i<MAX;i++){ fac[i]=fac[i-1]*i%mod; inv[i]=qp(fac[i],mod-2); } } int main(){ int i; init(); len=(int)sqrt(MAX); scanf("%d",&t); int L=1,R=0; long long res=1; for(i=0;i<t;i++){ scanf("%d%d",&q[i].r,&q[i].l); q[i].id=i; } sort(q,q+t,cmp); for(i=0;i<t;i++){ while(R<q[i].r){ R++; res=res*2%mod; res=(res-C(R-1,L)+mod)%mod; } while(L<q[i].l){ res=(res+C(R,L+1))%mod; L++; } while(R>q[i].r){ res=(res+C(R-1,L)+mod)%mod; res=res*inv[2]%mod; R--; } while(L>q[i].l){ L--; res=(res-C(R,L+1)+mod)%mod; } ans[q[i].id]=res; } for(i=0;i<t;i++) printf("%lld\n",ans[i]); return 0; }
1003:Problem C. Problems on a Tree
并查集维护easy+medium题目联通块的大小和与之相隔一个hard题目的easy联通块的大小和
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> #include <cmath> #include <vector> #include <iostream> #include <stack> #include <set> #include <map> #define LSON l,m,x<<1 #define RSON m+1,r,x<<1|1 using namespace std; const int MAX=5e5+5; struct node{ int to,val,nxt; }edge[MAX]; int head[MAX],cnt; int fa[MAX],dep[MAX],sch[MAX]; int f1[MAX],f2[MAX],sz2[MAX]; int root1(int x){ if(f1[x]==x) return x; else return f1[x]=root1(f1[x]); } int root2(int x){ if(f2[x]==x) return x; else return f2[x]=root2(f2[x]); } void unite1(int x, int y){ int fx=root1(x); int fy=root1(y); f1[fy]=fx; sch[fx]+=sch[fy]; } void unite2(int x,int y){ int fx=root2(x); int fy=root2(y); f2[fy]=fx; sz2[fx]+=sz2[fy]; sch[root1(fa[fy])]-=sz2[fy]; if(fa[fx]) sch[root1(fa[fx])]+=sz2[fy]; } void dfs(int x,int ff){ fa[x]=ff;dep[x]=dep[ff]+1; for(int i=head[x];i;i=edge[i].nxt){ int to=edge[i].to; if(to==ff) continue; sch[x]++; dfs(to,x); } for(int i=head[x];i;i=edge[i].nxt){ int to=edge[i].to; if(to==ff) continue; if(edge[i].val==1) unite1(x,to),unite2(x,to); else if(edge[i].val==2) unite2(x,to); } } void update(int x,int y){ if(root1(x)==root1(y)) return ; x=root1(x);y=root1(y); if(dep[x]<dep[y]) swap(x,y); int z=root1(fa[x]); if(root2(x)==root2(z)) unite1(z,x); else unite2(z,x); } int t,n,m; int ans; int main(){ int i; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); ans=cnt=0; for(i=1;i<=n;i++){ f1[i]=f2[i]=i; head[i]=fa[i]=sch[i]=dep[i]=0; sz2[i]=1; } for(i=1;i<n;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); edge[++cnt]=(node){u,w,head[v]}; head[v]=cnt; edge[++cnt]=(node){v,w,head[u]}; head[u]=cnt; } dep[0]=0; dfs(1,0); for(i=0;i<m;i++){ int a,b,s,t; scanf("%d%d%d%d",&a,&b,&t,&s); update(a,b); int flag=0; if(root2(s)==root2(t)) flag=1; if(root1(fa[root2(s)])==root1(t)) flag=1; if(root2(fa[root1(t)])==root2(s)) flag=1; ans=sch[root1(t)]+sz2[root2(t)]+(root2(fa[root1(t)])==root2(t)?0:sz2[root2(fa[root1(t)])]); printf("%d %d\n",flag,ans); } } return 0; }
1004:Problem D. Nothing is Impossible
如果仅有 1 道题,至少有一个人做对这题需要有 错误答案个数 + 1 个人。 那么容易发现在每道题正确答案只有一个的情况下,如果 n道题中存在 s道题,使得学生人数 m不少于每道题 错误答案个数 + 1 相乘的 结果,那么一定有人能够得到 s 分。故我们将题目按错误答案个数从小到大排序,找到大的 p 满足∏(i<=p)(bi+1)就是答案。
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 6 using namespace std; 7 8 struct Data{ 9 int a,b; 10 bool operator <(const Data &T)const{ 11 return a*T.b>b*T.a; 12 } 13 }x[101]; 14 15 int n,m; 16 17 int main(){ 18 int T; 19 scanf("%d",&T); 20 while(T--){ 21 scanf("%d%d",&n,&m); 22 int now=m,ans=0,flag=0; 23 for(int i=1;i<=n;i++) 24 scanf("%d%d",&x[i].a,&x[i].b); 25 sort(x+1,x+1+n); 26 for(int i=1;i<=n;i++){ 27 int sum=x[i].a+x[i].b; 28 now/=sum; 29 if(now<=0){ 30 flag=1; 31 printf("%d\n",i-1); 32 break; 33 } 34 } 35 if(flag==0) printf("%d\n",n); 36 } 37 return 0; 38 }
1005:Problem E. Matrix from Arrays
找到矩阵的规律后发现如果L为奇数则大矩阵为大小为L*L的小矩阵拼成,如果L为偶数则为矩阵大小为2L*2L的小矩阵拼成。
对于答案要求的一个子矩阵,考虑划分成包括左上角(1,1)的四个矩阵进行加减的结果即可。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<algorithm> 5 6 using namespace std; 7 8 typedef long long LL; 9 10 LL sum[200][200]; 11 int A[15],a[201][201]; 12 int n,q,L; 13 14 LL calc(int x,int y){ 15 LL ans=0; 16 int a=x/L,b=y/L; 17 int c=x%L,d=y%L; 18 ans=sum[L][L]*a*b; 19 ans+=sum[L][d]*a; 20 ans+=sum[c][L]*b; 21 ans+=sum[c][d]; 22 return ans; 23 } 24 25 int main(){ 26 #ifndef ONLINE_JUDGE 27 freopen("E.in","r",stdin); 28 freopen("E.out","w",stdout); 29 #endif 30 int T; 31 scanf("%d",&T); 32 while(T--){ 33 scanf("%d",&n); 34 if(n&1) L=n; 35 else L=n*2; 36 for(int i=0;i<n;i++) 37 scanf("%d",&A[i]); 38 int cursor = 0; 39 for (int i = 0; i <= 200; ++i) { 40 for (int j = 0; j <= i; ++j) { 41 a[j+1][i - j+1] = A[cursor]; 42 cursor = (cursor + 1) % n; 43 } 44 } 45 sum[1][1]=a[1][1]; 46 for(int i=2;i<=L;i++) sum[i][1]=sum[i-1][1]+a[i][1]; 47 for(int i=2;i<=L;i++) sum[1][i]=sum[1][i-1]+a[1][i]; 48 for(int i=2;i<=L;i++) 49 for(int j=2;j<=L;j++) 50 sum[i][j]=-sum[i-1][j-1]+sum[i][j-1]+sum[i-1][j]+a[i][j]; 51 scanf("%d",&q); 52 for(int i=1;i<=q;i++){ 53 int x0,y0,x1,y1,x,y; 54 scanf("%d%d%d%d",&x0,&y0,&x1,&y1); 55 x0++; y0++; x1++; y1++; 56 LL ans=calc(x1,y1)-calc(x0-1,y1)-calc(x1,y0-1)+calc(x0-1,y0-1); 57 printf("%lld\n",ans); 58 } 59 } 60 return 0; 61 }
1007:Problem G. Depth-First Search
十分巧妙的计数问题。由于要求字典序更小的,故按位来考虑贡献。
记f[x]表示以x为根的子树能产生的dfs序的数量,有f[x]=|son[x]|!∏f[y] (y为x的孩子)。
故能两边dfs算出首位比b[1]小的贡献,然后再一遍dfs按位来确定贡献。
详见代码内注释。
1 #include<bits/stdc++.h> 2 3 #define maxn 2000000+5 4 5 using namespace std; 6 7 typedef long long LL; 8 9 const int mod=1e9+7; 10 11 vector <int> s[maxn],ord[maxn]; 12 13 LL fac[maxn],g[maxn],f[maxn]; 14 int b[maxn],son[maxn]; 15 int n,ind; 16 LL res; 17 18 LL sum(int x,int p){ 19 LL res=0; 20 while(p){ 21 res=(res+ord[x][p])%mod; 22 p-=p&-p; 23 } 24 return res; 25 } 26 27 void add(int x,int p,int val){ 28 while(p<=son[x]){ 29 ord[x][p]=((ord[x][p]+val)%mod+mod)%mod; 30 p+=p&-p; 31 } 32 } 33 34 LL qpow(LL x,LL p){ 35 LL res=1,base=x; 36 while(p){ 37 if(p&1) res=res*base%mod; 38 base=base*base%mod; 39 p>>=1; 40 } 41 return res; 42 } 43 44 45 void dfs(int x,int fa){ 46 f[x]=1; 47 if(x!=b[1]) s[x].erase(find(s[x].begin(),s[x].end(),fa)); 48 son[x]=s[x].size(); s[x].insert(s[x].begin(),0); 49 for(int i=0;i<=son[x];i++) ord[x].push_back(0); 50 for(int i=1;i<=son[x];i++){ 51 dfs(s[x][i],x); 52 f[x]=f[x]*f[s[x][i]]%mod; 53 } 54 for(int tmp=1,i=1;i<=son[x];i++){ 55 g[s[x][i]]=tmp; 56 tmp=tmp*f[s[x][i]]%mod; 57 } 58 for(int tmp=1,i=son[x];i>=1;i--){ 59 g[s[x][i]]=g[s[x][i]]*tmp%mod; 60 tmp=tmp*f[s[x][i]]%mod; 61 } 62 //g[x]表示x的所有兄弟的f[]的乘积 63 f[x]=f[x]*fac[son[x]]%mod; 64 } 65 66 void calcpre(int x,LL val){ 67 //计算以比b[1]小的节点为根 68 //v为换根后x的父节点的f[]值 69 if(x<b[1]) res=(res+val*(son[x]+1)%mod*f[x]%mod)%mod; //计算以x为根的树的贡献 70 for(int i=1;i<=son[x];i++){ 71 if(x==b[1]) calcpre(s[x][i],val*fac[son[x]-1]%mod*g[s[x][i]]%mod); 72 else calcpre(s[x][i],val*fac[son[x]]%mod*g[s[x][i]]%mod); 73 } 74 } 75 76 int findnr(int x,int k){//找到p的孩子里比x小的最大的那个节点 77 int pos=0; 78 for(int i=20;i>=0;i--) 79 if(pos+(1<<i)<=son[x] && s[x][pos+(1<<i)]<k) pos+=(1<<i); 80 return pos; 81 } 82 83 int calcnow(int x,LL val){ 84 LL now=1; ind++; 85 for(int i=1;i<=son[x];i++) now=now*f[s[x][i]]%mod,add(x,i,1); 86 for(int i=son[x]-1;i>=0;i--){ 87 int p=findnr(x,b[ind+1]); 88 res=(res+val*fac[i]%mod*now%mod*sum(x,p)%mod)%mod; 89 //先遍历名次小于等于s的这些子树中的某一颗,之后的子树都可随意安排(s为名次小于b序列当前位置的最大元素) 90 //v表示之前的除了已确定的以外也是随意安排的方案数 91 p++; 92 if(p>son[x] || s[x][p]!=b[ind+1]) return 1; 93 //若无法按照b序列继续生成dfs序,直接返回 94 add(x,p,-1); now=now*qpow(f[s[x][p]],mod-2)%mod; 95 //按照b序列生成dfs序,然后删掉这颗子树会产生的贡献 96 if(calcnow(s[x][p],val*now%mod*fac[i]%mod)) return 1; 97 } 98 return 0; 99 } 100 101 int main(){ 102 int T; 103 scanf("%d",&T); fac[0]=1; 104 for(int i=1;i<maxn;i++) fac[i]=fac[i-1]*i%mod; 105 while(T--){ 106 scanf("%d",&n); res=0; ind=0; 107 for(int i=1;i<=n;i++){ 108 scanf("%d",&b[i]); 109 s[i].clear(); ord[i].clear(); 110 son[i]=0; 111 } 112 for(int i=1;i<n;i++){ 113 int x,y; 114 scanf("%d%d",&x,&y); 115 s[x].push_back(y); 116 s[y].push_back(x); 117 } 118 for(int i=1;i<=n;i++) 119 sort(s[i].begin(),s[i].end()); 120 dfs(b[1],0); 121 calcpre(b[1],1); 122 calcnow(b[1],1); 123 printf("%lld\n",res); 124 } 125 return 0; 126 }
1010:Problem J. Let Sudoku Rotate
一顿模拟加搜索就可以了,加一个小小的剪枝
#include <cstdio> #include <cstdlib> #include <iostream> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <vector> #include <string> #include <map> using namespace std; int sudoku[20][20]; int raw[20][20],col[20][20]; int ans; void init() { memset(raw,0,sizeof(raw)); memset(col,0,sizeof(col)); ans=0x7fffff; } int w[20][20]; void roat(int a,int b)//顺时针旋转矩阵90度 { for(int i=0;i<4;i++) for(int j=0;j<4;j++) w[j][3-i]=sudoku[a+i][b+j]; for(int i=0;i<4;i++) for(int j=0;j<4;j++) sudoku[a+i][b+j]=w[i][j]; } int vis[20],tot; bool check(int a,int b) { for(int i=a;i<a+4;i++) { tot++; for(int j=0;j<b+4;j++) { if(vis[sudoku[i][j]]==tot) return 0; vis[sudoku[i][j]]=tot; } } for(int i=b;i<b+4;i++) { tot++; for(int j=0;j<a+4;j++) { if(vis[sudoku[j][i]]==tot) return 0; vis[sudoku[j][i]]=tot; } } return 1; } void dfs(int x,int y,int step) { if(x==4) { ans=min(ans,step); return; } if(step>=ans) return; if(y==4) return dfs(x+1,0,step); for(int i=0;i<4;i++) { if(check(x*4,y*4)) dfs(x,y+1,step+i); roat(x*4,y*4); } } int solve(void) { int i,j; char str[20]; for(i=0;i<16;i++) { scanf("%s",str); for(j=0;j<16;j++) { if(str[j]>='0'&&str[j]<='9') sudoku[i][j]=str[j]-'0'; else if(str[j]=='A') sudoku[i][j]=10; else if(str[j]=='B') sudoku[i][j]=11; else if(str[j]=='C') sudoku[i][j]=12; else if(str[j]=='D') sudoku[i][j]=13; else if(str[j]=='E') sudoku[i][j]=14; else if(str[j]=='F') sudoku[i][j]=15; else; } } tot=0; dfs(0,0,0); printf("%d\n",ans); } int main() { int T; scanf("%d",&T); while(T--) { init(); solve(); } return 0; }
1011:Problem K. Expression in Memories
分类讨论一下不合法的情况,然后把所有的?变成1来构造
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cctype> 5 #include<algorithm> 6 7 #define maxn 500+5 8 9 using namespace std; 10 11 int mk[maxn]; 12 char s[maxn]; 13 14 int main(){ 15 int T; 16 scanf("%d",&T); 17 while(T--){ 18 19 scanf("%s",s+1); 20 int len=strlen(s+1); 21 for(int i=1;i<len;i++) 22 if((s[i-1]=='+' || s[i-1]=='*' || i==1) && s[i]=='0' && s[i+1]=='?'){ 23 s[i+1]='+'; 24 } 25 int flag=1; 26 for(int i=1;i<len;i++) 27 if((s[i]=='+' || s[i]=='*') && (s[i+1]=='*' || s[i+1]=='+')){ 28 flag=0; 29 break; 30 } 31 if(s[len]=='+' || s[len]=='*') flag=0; 32 if(s[1]=='+' || s[1]=='*') flag=0; 33 for(int i=1;i<len;i++) 34 if(s[i-1]!='0' && s[i]=='0' && isdigit(s[i+1]) && (s[i-1]!='?' && !(s[i-1]>='1' && s[i-1]<='9'))){ 35 flag=0; break; 36 } 37 if(!flag){ 38 puts("IMPOSSIBLE"); 39 continue; 40 } 41 42 for(int i=1;i<=len;i++) 43 if(s[i]=='?') s[i]='1'; 44 for(int i=1;i<=len;i++) 45 printf("%c",s[i]); 46 puts(""); 47 } 48 return 0; 49 }
1012:Problem L. Graph Theory Homework
易证直接从1走到n是最近的,证明方法和证均值不等式一样
#include <cstdio> #include <cstdlib> #include <iostream> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <vector> #include <string> #include <map> using namespace std; int sudoku[20][20]; int raw[20][20],col[20][20]; int ans; void init() { memset(raw,0,sizeof(raw)); memset(col,0,sizeof(col)); ans=0x7fffff; } int w[20][20]; void roat(int a,int b)//顺时针旋转矩阵90度 { for(int i=0;i<4;i++) for(int j=0;j<4;j++) w[j][3-i]=sudoku[a+i][b+j]; for(int i=0;i<4;i++) for(int j=0;j<4;j++) sudoku[a+i][b+j]=w[i][j]; } int vis[20],tot; bool check(int a,int b) { for(int i=a;i<a+4;i++) { tot++; for(int j=0;j<b+4;j++) { if(vis[sudoku[i][j]]==tot) return 0; vis[sudoku[i][j]]=tot; } } for(int i=b;i<b+4;i++) { tot++; for(int j=0;j<a+4;j++) { if(vis[sudoku[j][i]]==tot) return 0; vis[sudoku[j][i]]=tot; } } return 1; } void dfs(int x,int y,int step) { if(x==4) { ans=min(ans,step); return; } if(step>=ans) return; if(y==4) return dfs(x+1,0,step); for(int i=0;i<4;i++) { if(check(x*4,y*4)) dfs(x,y+1,step+i); roat(x*4,y*4); } } int solve(void) { int i,j; char str[20]; for(i=0;i<16;i++) { scanf("%s",str); for(j=0;j<16;j++) { if(str[j]>='0'&&str[j]<='9') sudoku[i][j]=str[j]-'0'; else if(str[j]=='A') sudoku[i][j]=10; else if(str[j]=='B') sudoku[i][j]=11; else if(str[j]=='C') sudoku[i][j]=12; else if(str[j]=='D') sudoku[i][j]=13; else if(str[j]=='E') sudoku[i][j]=14; else if(str[j]=='F') sudoku[i][j]=15; else; } } tot=0; dfs(0,0,0); printf("%d\n",ans); } int main() { int T; scanf("%d",&T); while(T--) { init(); solve(); } return 0; }