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;
}
View Code

 

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;
}
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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;
}
View Code

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 }
View Code

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;
}
View Code

 

posted @ 2018-08-05 09:36  Hetui  阅读(155)  评论(0编辑  收藏  举报