2018 Multi-University Training Contest 3

1001:Problem A. Ascending Rating

 首先可以通过单调栈维护出每个位置右边第一个大于自己的数所在的位置,记为rightmax[i],然后维护一个双端队列。双端队列里维护[L,L+m-1]中的最长上升子序列,首先队首很好维护,只要判断当前head的

rightmax[]是不是在范围了内就行。队尾每当出队一个元素,需要按rightmax[]递归入队一个上升子序列。

因为每个元素最多只能入队出对一次,所以维护过程是O(2n)

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<deque>
#include<queue>
#include<stack>
#include<algorithm>

#define maxn 10000000+5

using namespace std;

struct FastIO
{
    static const int S = 200;
    int wpos;
    char wbuf[S];
    FastIO() :wpos(0) {}
    inline int xchar()
    {
        static char buf[S];
        static int len = 0, pos = 0;
        if (pos == len) pos = 0, len = fread(buf, 1, S, stdin);
        if (pos == len) exit(0);
        return buf[pos++];
    }
    inline int read()
    {
        int s = 1, c = xchar(), x = 0;
        while (c <= 32) c = xchar();
        if (c == '-') s = -1, c = xchar();
        for (; '0' <= c && c <= '9'; c = xchar()) x = x * 10 + c - '0';
        return x * s;
    }
    ~FastIO()
    {
        if (wpos) fwrite(wbuf, 1, wpos, stdout), wpos = 0;
    }
}io;

deque <int> qu;

int a[maxn], nxt[maxn], st[maxn], sst[maxn];
int n, m, k, p, q, r, mod;
long long ans1, ans2;

void find(int now, int lim) {
    if (nxt[now] != -1 && nxt[now] < lim) find(nxt[now], lim);
    if (lim != now) qu.push_front(now);
}

void find2(int now,int lim){
    if (nxt[now] != -1 && nxt[now] - lim + 1 <= m) find2(nxt[now], lim);
    qu.push_front(now);
}

int main() {
    int T, top, l, r;
    T = io.read();
//    scanf("%d",&T);
    while (T--) {
        n = io.read(); m = io.read(); k = io.read(); p = io.read(); q = io.read(); r = io.read(); mod = io.read();
    //    scanf("%d%d%d%d%d%d%d",&n,&m,&k,&p,&q,&r,&mod);
        for (int i = 1; i <= k; i++) a[i] = io.read();
//        for (int i = 1; i <= k; i++) scanf("%d", &a[i]);
        for (int i = k + 1; i <= n; i++) a[i] = ((long long)p*a[i - 1] + (long long)q*i + r) % mod;
        //    memset(nxt,0,sizeof(int)*(n+1));
        top = 0; ans1 = ans2 = 0;
        for (int i = 1; i <= n; i++) {
            while (top && a[st[top]] < a[i]) nxt[st[top]] = i, top--;
            st[++top] = i;
        }
        while (top) nxt[st[top]] = -1, top--;
        while (!qu.empty()) qu.pop_back();
        qu.push_back(1);
        int x = 0;
        while (qu.front() <= n && !qu.empty()) {
            while (nxt[qu.back()]>0 && nxt[qu.back()] - qu.front() + 1 <= m) {
                qu.push_back(nxt[qu.back()]);
            }
            x = qu.front();

            ans1 += a[qu.back()]^x; ans2 += qu.size()^x;
            qu.pop_front();
            if (x == n - m + 1) break;
            
            if (!qu.empty()) {
                int now=x+1, lim = qu.front(), top2=0;
                if (lim != now) sst[++top2] = now;
                while (nxt[now] != -1 && nxt[now] < lim) {
                    now = nxt[now];
                    if (lim != now) sst[++top2] = now;
    
                }
                while (top2) qu.push_front(sst[top2]),top2--;
            }
            else {
                int now = x + 1, lim = x+1, top2 = 0;
                sst[++top2] = now;
                while (nxt[now] != -1 && nxt[now] - lim + 1 <= m) {
                    now = nxt[now];
                    sst[++top2] = now;
                    
                }
                while (top2) qu.push_front(sst[top2]),top2--;
                    
            }
        }
        printf("%lld %lld\n", ans1, ans2);
    }
    return 0;
}
View Code

1003: Problem C. Dynamic Graph Matching

 状压dp,从大到小状态遍历,如果两个节点都没匹配,就加上方案数,删边时考虑到加边的顺序可以更改,看作最后加的一条边是需要删的边,从而遍历减去影响即可。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<deque>
#include<queue>
#include<stack>
#include<algorithm>

const int MAX=1e4+5;
const int mod=1e9+7;
int t,n,m;
int a,b,now,f[MAX],cnt[MAX],ans[MAX];
char op;

int main(){
    int i;
    
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        now=1<<n;
        for(i=0;i<now;i++) cnt[i]=__builtin_popcount(i);
        memset(f,0,sizeof(f));
        f[0]=1;
        while(m--){
            getchar();
            scanf("%c%d%d",&op,&a,&b);
            a--,b--;
            int S=(1<<a)|(1<<b);
            if(op=='+'){
                for(i=now-1;i>=0;i--) if(!(i&S)) f[S^i]=(f[S^i]+f[i])%mod;
            }
            else
                for(i=0;i<now;i++) if(!(i&S)) f[S^i]=(f[S^i]-f[i]+mod)%mod;
            memset(ans,0,sizeof(ans));
            for(i=0;i<now;i++) ans[cnt[i]]=(ans[cnt[i]]+f[i])%mod;
            for(i=2;i<=n;i+=2) printf("%d%c",ans[i],i==n?'\n':' ');
        }
    }
    
    return 0;    
}
View Code

1004:Problem D. Euler Function

 打个表吧,很容易看出规律。大于6的数的欧拉函数一定是合数

欧拉函数是积性函数,而除了2,3,质数的欧拉函数是偶数,所以除了个别数,欧拉函数都是合数

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <math.h>
#include <vector>
using namespace std;

//返回某个数的欧拉值 
int t,n;

int main(){
    int i;
    
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        if(n>2) printf("%d\n",n+5);
        else if(n==1) puts("5");
        else if(n==2) puts("7");
    }
    
    return 0;
} 
View Code

1006:Problem F. Grab The Tree

 结论题,首先按层将树分成两部分,如果有一部分异或和大于另外一部分,那么先手拿这部分就行了,如果相等,因为是异或所以取一个节点和放一个节点影响一样,所以一定是平局

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

#define maxn 100000+5

using namespace std;

struct Edge{
    int u,v,nxt;    
}e[maxn<<1];

int head[maxn],w[maxn];
int n,ind,sum,all;

void addedge(int x,int y){
    e[ind]=(Edge){x,y,head[x]},head[x]=ind++;
    e[ind]=(Edge){y,x,head[y]},head[y]=ind++;
}

void dfs(int x,int pa,int dep){
    if(dep&1) sum^=w[x];
    for(int i=head[x];i!=-1;i=e[i].nxt)
        if(e[i].v!=pa)
            dfs(e[i].v,x,dep+1);
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        memset(head,-1,sizeof(head));
        scanf("%d",&n); ind=all=0;
        for(int i=1;i<=n;i++)
            scanf("%d",&w[i]),all^=w[i];
        for(int i=1;i<n;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            addedge(x,y);
        }
        sum=0;
        dfs(1,0,1);
        if((all^sum)==sum) puts("D");
        else puts("Q");
    }
    
    return 0;
}
View Code

1007: Problem G. Interstellar Travel

 由于两点之间移动的代价为叉积,想到转化为面积:把路径从n走回1,得到一个顺时针走的封闭的多边形,该多边形的面积为题中代价的相反数,故要代价最低就要使得该多边形面积最大,即求图中所给点构成的凸包的面积。

由于要字典序最小的路径,首先把所有坐标相同的点只取标号最小的,之后求出凸包后,根据叉积判断某点是否为拐点,两拐点之间同一直线上的点则倒序求出一段递减的标号即可。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<algorithm>
 5 
 6 #define maxn 200000+5
 7 
 8 using namespace std;
 9 
10 struct Point{
11     int x,y,p;
12     Point(){ }
13     Point(int a,int b,int c){
14         x=a; y=b; p=c;
15     }
16     bool operator < (const Point &T)const{
17         if(x!=T.x) return x<T.x;
18         if(y!=T.y) return y>T.y;
19         return p<T.p;
20     }
21     bool operator != (const Point &T)const{
22         if(x!=T.x || y!=T.y) return true;
23         return false;
24     }
25     friend Point operator - (const Point &A,const Point &B){
26         return Point(A.x-B.x,A.y-B.y,A.p);
27     }
28 }p[maxn],res[maxn],ans[maxn];
29 
30 typedef Point Vector;
31 
32 double Cross(const Vector& A, const Vector& B) { return (long long)A.x*B.y - (long long)A.y*B.x; }
33 
34 int inp[maxn],lsp[maxn]; 
35 int n,t,r;
36 
37 int main(){
38 #ifndef ONLINE_JUDGE
39     freopen("G.in","r",stdin);
40     freopen("G.out","w",stdout);
41 #endif
42     int T;
43     scanf("%d",&T);
44     while(T--){
45         memset(inp,false,sizeof(inp));
46         scanf("%d",&n);
47         for(int i=1;i<=n;i++){
48             scanf("%d%d",&p[i].x,&p[i].y);
49             p[i].p=i;
50         }    
51         sort(p+1,p+1+n);
52         t=0;
53         for(int i=1;i<=n;i++){
54             if(i!=1 && !(p[i]!=p[i-1])) continue;
55             while(t>1 && Cross(res[t]-res[t-1],p[i]-res[t-1])>0) t--;
56             res[++t]=p[i];
57         }
58         inp[1]=inp[t]=1;
59         for(int i=2;i<t;i++)
60             if(res[i]!=res[i-1] && Cross(res[i]-res[i-1],res[i+1]-res[i])!=0) inp[i]=1;
61         for(int i=t;i>=1;i--)
62             if(inp[i]) lsp[i]=i;
63             else if(res[i].p<res[lsp[i+1]].p) lsp[i]=i;
64             else lsp[i]=lsp[i+1];
65             
66         r=0;
67         for(int i=1;i<=t;i++)
68             if(lsp[i]==i) ans[++r]=res[i];
69         for(int i=1;i<r;i++)
70             printf("%d ",ans[i].p);
71         printf("%d",ans[r].p);
72         puts("");
73     }
74     return 0;
75 }
View Code

 

1008:Problem H. Monster Hunter

 较为经典的贪心问题。

 首先考虑没有树的限制,分两种情况(优先第一种):

 1.a<b,先选a较小的那一个

 2.a>=b,先选b较大的那一个

 在有树的限制的情况下,我们发现这个二元组(a,b)是可以进行合并的,即(a,b)+(c,d) -> (max(a,a-b+c),max(a,a-b+c)+b-a+c-d) ,故当一个节点为其父节点的所有子节点中最优的那个时,我们选完父节点必然会马上选它,故它和它的父亲节点就可以合并,这个操作可以用并查集实现,而每次需要挑出最优的一个节点和它的父节点合并,这个操作可以使用set或者优先队列实现。

(这种贪心方式都适用于有全序关系且可以合并的二元组)

  1 #pragma comment(linker, "/STACK:102400000,102400000")
  2 
  3 #include<queue>
  4 #include<map>
  5 #include<set>
  6 #include<cstdio>
  7 #include<cstring>
  8 #include<cstdlib>
  9 #include<algorithm>
 10 
 11 #define maxn (100000+5)
 12 
 13 using namespace std;
 14 
 15 struct Edge {
 16     int u, v, nxt;
 17     Edge(){}
 18     Edge(int x,int y,int z) {
 19         u = x; v = y; nxt = z;
 20     }
 21 }e[maxn << 1];
 22 
 23 struct Data {
 24     long long a, b;
 25     Data(){}
 26     Data(long long x, long long y) {
 27         a = x; b = y;
 28     }
 29     Data unite(const Data &y)const {
 30         long long need = max(a, a - b + y.a);
 31         return Data ( need, need + b - a + y.b - y.a );
 32     }
 33     friend bool operator <(const Data &x, const Data &y) {
 34         if (x.a<x.b && y.a >= y.b) return 0;
 35         if (x.a >= x.b && y.a<y.b) return 1;
 36         if (x.a<x.b && y.a<y.b) return x.a>y.a;
 37         if (x.a >= x.b && y.a >= y.b) return x.b<y.b;
 38     }
 39 }a[maxn];
 40 
 41 typedef pair <Data,int> pdi;
 42 
 43 set <pdi> mp;
 44 
 45 int head[maxn], d[maxn], fa[maxn];
 46 int ind, n;
 47 int T;
 48 
 49 void addedge(int x, int y) {
 50     e[ind] = Edge ( x, y, head[x] ), head[x] = ind++;
 51     e[ind] = Edge ( y, x, head[y] ), head[y] = ind++;
 52 }
 53 
 54 void init() {
 55     mp.clear(); ind = 0;
 56     for (int i = 0; i <= n; i++) head[i] = -1;
 57     for (int i = 1; i <= n; i++) d[i] = i;
 58 }
 59 
 60 void dfs(int x, int p) {
 61     for (int i = head[x]; i != -1; i = e[i].nxt)
 62         if (e[i].v!=p) {
 63             fa[e[i].v] = x;
 64             dfs(e[i].v, x);
 65         }
 66 }
 67 
 68 int find(int x) {
 69     if (d[x] == x) return x;
 70     return d[x] = find(d[x]);
 71 }
 72 
 73 int main() {
 74 #ifndef ONLINE_JUDGE
 75     freopen("H.in", "r", stdin);
 76     freopen("H.out","w",stdout);
 77 #endif
 78     scanf("%d", &T);
 79     while (T--) {
 80         scanf("%d", &n);
 81         init();
 82         a[1] = Data(0,0);
 83         for (int i = 2; i <= n; i++) {
 84             long long x, y;
 85             scanf("%lld%lld", &x, &y);
 86             a[i] = Data(x,y);
 87             mp.insert(make_pair(Data(x,y),i));
 88         }
 89         for (int i = 1; i<n; i++) {
 90             int x, y;
 91             scanf("%d%d", &x, &y);
 92             addedge(x, y);
 93         }
 94         fa[1] = 1;
 95         dfs(1, 0);
 96         while (!mp.empty()) {
 97             set<pdi>::iterator it = mp.end(); it--;
 98             Data x = it->first, y;
 99             int pos = it->second;
100             mp.erase(it);
101             y = a[find(fa[pos])]; mp.erase(make_pair(y,d[fa[pos]]));
102             y = y.unite(x);
103             int i = find(fa[pos]), j = find(pos);
104             d[j] = d[i]; a[d[i]] = y; 
105             if(d[i]!=1) mp.insert(make_pair(y,d[i]));
106         }
107             printf("%lld\n",a[1].a);
108     }
109     return 0;
110 }
View Code

 

1009:Problem I. Random Sequence

很有意思的概率DP,官方题解很详细

f[i][x][y][z]表示考虑前i个位置,a i =x,gcd(a i ,a i1 )=y,gcd(a i ,a i1 ,a i2 )=z的期望,枚举a i+1 的值转移即可。

时间复杂度O(nmS),其中S表示(x,y,z)的状态数。显然合法状态中yx,zy,当m=100时S=1471。

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#include <string>
#include <map>
using namespace std;
const long long mod=1e9+7;

long long dp[2][110][110][110];
int ai[110],vi[110],biao[110][110],le[110],ri[110];
vector <int> fac[110];
int n,m;

void init()
{
    memset(dp,0,sizeof(dp));
    for(int i=le[3];i<=ri[3];i++)
    {
        for(int j=le[2];j<=ri[2];j++)
        {
            for(int k=le[1];k<=ri[1];k++)
                dp[0][i][biao[i][j]][biao[k][biao[i][j]]]++;
        }
    }
}

int gcd(int a,int b)
{
    if(b==0) return a;
    return gcd(b,a%b);
}

long long qp(long long p,long long q)
{
    long long cnt=1;
    while(q>0)
    {
        if(q%2==1) cnt=(cnt*p)%mod;
        q/=2;
        p=(p*p)%mod;
    }
    return cnt%mod;
}

int main()
{
    int i,j;
    for(i=1;i<=100;i++)
    {
        for(j=1;j<=100;j++) biao[i][j]=gcd(i,j);
    }
    for(i=1;i<=100;i++)
    {
        for(j=i;j<=100;j+=i) fac[j].push_back(i);
    }
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
        {
            scanf("%d",&ai[i]);
            if(ai[i]==0)
            {
                le[i]=1;
                ri[i]=m;
            }
            else le[i]=ri[i]=ai[i];
        }
        for(i=1;i<=m;i++)  scanf("%d",&vi[i]);
        init();
        int xx=0;
        //cout<<"1"<<endl;
        for(i=4;i<=n;i++)
        {
            int x,y,z;
            for(x=1;x<=m;x++)
            {
                for(vector<int>::iterator iy=fac[x].begin();iy!=fac[x].end();iy++)
                {
                    y=*iy;
                    for(vector<int>::iterator iz=fac[y].begin();iz!=fac[y].end();iz++)
                    {
                        z=*iz;
                       // cout<<x<<" "<<y<<" "<<z<<endl;
                        if(dp[xx][x][y][z]==0) continue;
                        for(j=le[i];j<=ri[i];j++)
                        {
                            dp[xx^1][j][biao[j][x]][biao[j][y]]+=dp[xx][x][y][z]*vi[biao[j][z]];
                            dp[xx^1][j][biao[j][x]][biao[j][y]]%=mod;
                        }
                        dp[xx][x][y][z]=0;
                    }
                }
            }
            xx^=1;
        }
        long long ans=0;
        int x,y,z;
            for(x=1;x<=m;x++)
            {
                for(vector<int>::iterator iy=fac[x].begin();iy!=fac[x].end();iy++)
                {
                    y=*iy;
                    for(vector<int>::iterator iz=fac[y].begin();iz!=fac[y].end();iz++)
                    {
                        z=*iz;
                       // cout<<x<<" "<<y<<" "<<z<<endl;
                        ans=(ans+dp[xx][x][y][z])%mod;
                    }
                }
            }
        long long tmp=qp(m,mod-2);
        for(i=1;i<=n;i++)
        {
            if(ai[i]==0) ans=(ans*tmp)%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

1012:Problem L. Visual Cube

 画家算法,模拟一下即可。从下到上,从里到外,从左到右,一个一个的小立方体画就行了

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <math.h>
#include <vector>
using namespace std;

const int MAX=200;
int t,a,b,c;
int mp[MAX][MAX];

int main(){
    int i,j,p,cnt;
    int x,y;
    
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&a,&b,&c);
        for(i=0;i<MAX;i++)
            for(j=0;j<MAX;j++)
                mp[i][j]='.';
        x=2*c+1,y=2*b;
        for(p=0;p<max(b+1,2);p++){
            for(i=x;i>x-2*c-1;i-=2)
                for(j=y;j<y+2*a+1;j+=2)
                    mp[i][j]='+';
            x+=2,y-=2;
        }    
        x=1,y=2*b+1;
        for(p=0;p<=max(b+1,2);p++){
            for(j=y;j<=y+2*a-1;j+=2)
                mp[x][j]='-';
                x+=2,y-=2;
        }
        x=2*b+1,y=0;
        for(p=0;p<c+1;p++){
            for(j=1;j<2*a;j+=2)
                mp[x][j]='-';
                x+=2,y-=2;
        }
        x=2,y=2*b+2*a,cnt=2;
        for(p=0;p<max(b+1,2);p++){
            for(i=x;i<x+2*c;i+=2)
                mp[i][y]='|';
            x+=2,y-=2; 
        }
        x=2*b+2,y=0;
        for(i=x;i<x+2*c;i+=2)
            for(j=y;j<2*a;j+=2) 
                mp[i][j]='|';
        
        x=2,y=2*b-1;
        for(p=0;p<b;p++){
                for(j=y;j<y+2*a+2;j+=2) 
                    mp[x][j]='/';
            x+=2,y-=2;
        }
        x=2,y=2*b+2*a-1;
        for(p=0;p<b;p++){
                for(i=x;i<x+2*c+2;i+=2) 
                    mp[i][y]='/';
            x+=2,y-=2;
        }
        
        
        for(i=1;i<1+2*b+2*c+1;i++){
            for(j=0;j<2*b+2*a+1;j++)
                printf("%c",mp[i][j]);
            putchar('\n');
        }
    }
    
    return 0;
} 
View Code

1013:Problem M. Walking Plan

看到本题自然想到用矩阵乘法来得到走k步由s到t的最短路,记为f[k][s][t],但这样做复杂度不过关。

发现k<=10000,故考虑对这个数组进行分块,设G[s][t]为恰好走1步的最短路,可用矩阵乘法算出恰好走i步的最短路A[i][s][t],

接着再用A[100][s][t]算出恰好走100*i步的最短路B[i][s][t]。然后将A[i][s][t]再进行一次floyd得到至少走i步的最短路。

最后枚举中间点i即可得到ans=min(A[k%100][s][i]+B[k/100][i][t])。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<algorithm>
 5 
 6 #define maxn 50+5
 7 
 8 using namespace std;
 9 
10 int n,m,q;
11 
12 struct Matrix{
13     int d[maxn][maxn];
14     Matrix (){ memset(d,0x3f,sizeof(d)); }
15     void init(){ 
16         memset(d,0x3f,sizeof(d));
17         for(int i=1;i<=n;i++) d[i][i]=0;
18     }
19     Matrix operator * (const Matrix &T)const{
20         Matrix temp;        
21         for(int i=1;i<=n;i++)            
22             for(int k=1;k<=n;k++)
23                 for(int j=1;j<=n;j++)
24                     temp.d[i][k]=min(temp.d[i][k],d[i][j]+T.d[j][k]);
25         return temp;
26     }
27 }a[105],b[105],g,e;
28 
29 int main(){
30 #ifndef ONLINE_JUDGE
31     freopen("M.in","r",stdin);
32     freopen("M.out","w",stdout);
33 #endif
34     int T;
35     scanf("%d",&T);
36     while(T--){
37         scanf("%d%d",&n,&m);
38         a[0].init(); b[0].init(); g.init();
39         memset(e.d,0x3f,sizeof(e.d));
40         for(int i=1;i<=m;i++){
41             int x,y,z;
42             scanf("%d%d%d",&x,&y,&z);
43             g.d[x][y]=e.d[x][y]=min(e.d[x][y],z);
44         }
45         for(int i=1;i<=100;i++) a[i]=a[i-1]*e;
46         for(int i=1;i<=100;i++) b[i]=b[i-1]*a[100];        
47         for(int k=1;k<=n;k++)
48             for(int i=1;i<=n;i++)
49                 for(int j=1;j<=n;j++)
50                     g.d[i][j]=min(g.d[i][j],g.d[i][k]+g.d[k][j]);
51         for(int i=0;i<=100;i++)    a[i]=a[i]*g;
52         scanf("%d",&q);
53         for(int i=1;i<=q;i++){
54             int s,t,k,A,B,ans=0x3f3f3f3f;
55             scanf("%d%d%d",&s,&t,&k);
56             A=k%100; B=k/100;
57             for(int j=1;j<=n;j++) 
58                 ans=min(ans,a[A].d[s][j]+b[B].d[j][t]);
59             if(ans!=0x3f3f3f3f) printf("%d\n",ans);
60             else puts("-1");
61         }
62     }
63     return 0;
64 }
View Code
posted @ 2018-07-31 19:46  Hetui  阅读(213)  评论(0编辑  收藏  举报