2018ACM-ICPC南京區域賽---AJGIDKM


含【最小球覆蓋】【最大流isap】模板。

題面pdf

https://codeforc.es/gym/101981/attachments/download/7891/20182019-acmicpc-asia-nanjing-regional-contest-en.pdf

 

G---Pyramid【數論】【規律】【遞推式】

題意:

度為$n$的Pyramid是一個由$\frac{n(n+1)}{2}$個三角形組成大三角形。比如度為3的Pyramid是下面這樣子。

現在由這些頂點組成等邊三角形,問有多少個。

思路:

zyn先放到坐標系里打了個表,然后發現差的差是一個等差數列....

於是就可以有遞推關系式了。矩陣快速冪T了

所以只能解方程,把系數解出來。

注意取模求逆元!

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const ll mod=1e9+7;
 5 ll n;
 6 ll fpow(ll a,ll n)
 7 {
 8     ll res=1,base=a%mod;
 9     while(n)
10     {
11         if(n&1) res*=base, res%=mod;
12         base*=base, base%=mod;
13         n>>=1;
14     }
15     return res%mod;
16 }
17 ll inv(ll a){return fpow(a,mod-2);}
18 int main()
19 {
20     int T;
21     cin>>T;
22     while(T--)
23     {
24         scanf("%I64d",&n);
25         ll ans=0;
26         ans+=fpow(n,4), ans%=mod;
27         ans+=6*fpow(n,3)%mod, ans%=mod;
28         ans+=11*fpow(n,2)%mod, ans%=mod;
29         ans+=6*n%mod, ans%=mod;
30         ans*=inv(24), ans%=mod;
31         printf("%I64d\n",ans);
32     }
33 }
View Code

 

I---Magic Potion【網絡流】

題意:

有$n$個英雄,$m$只怪物。每個英雄可以殺某些指定的怪物,但是他們只能殺一次。現在有$k$瓶葯水,喝了一瓶葯水就可以多殺一只怪物,但是每個英雄最多只能喝一瓶。問他們最多可以殺多少怪物。

思路:

想dp想了半天想不出來。丟給zyn他直接就說是網絡流。噢好有道理。

每個英雄和怪物之間有一條權值為1的邊,源點和英雄有一個權值為1的邊,怪物和匯點有權值唯一的邊。

這樣跑出來的最大流是不考慮喝葯水的情況的答案。

現在可以喝葯水了,相當於多了一個節點,源點到這個節點的邊權值是k,然后這個節點和每個英雄有權值是1的邊。

相當於給$k$個英雄多了一條1的流量,又限制了每個英雄只能喝一瓶。

聽說Dinic T了,所以后來自己直接套的isap的板子。【其實還並不很熟網絡流】

  1 #include<iostream>
  2 //#include<bits/stdc++.h>
  3 #include<cstdio>
  4 #include<cmath>
  5 #include<cstdlib>
  6 #include<cstring>
  7 #include<algorithm>
  8 #include<queue>
  9 #include<vector>
 10 #include<set>
 11 #include<climits>
 12 #include<map>
 13 using namespace std;
 14 typedef long long LL;
 15 #define N 100010
 16 #define pi 3.1415926535
 17 #define inf 0x3f3f3f3f
 18 
 19 const int maxn = 505;
 20 int n, m, k;
 21 struct edge{
 22     int v, w, nxt;
 23 }e[maxn* maxn + 5 * maxn];
 24 int h[maxn * 2], tot;
 25 int gap[maxn * 2], last[maxn * 2], d[maxn * 2], que[maxn * 2], ql, qr;
 26 
 27 void addedge(int u, int v, int w)
 28 {
 29     e[++tot] = (edge){v, w, h[u]};
 30     h[u] = tot;
 31     e[++tot] = (edge){u, 0, h[v]};
 32     h[v] = tot;
 33 }
 34 
 35 
 36 void init(int s, int t)
 37 {
 38     memset(gap, 0, sizeof(gap));
 39     memset(d, 0, sizeof(d));
 40     ++gap[d[t] = 1];
 41     for(int i = 1; i <= n + m + 3; i++){
 42         last[i] = h[i];
 43     }
 44     que[ql = qr = 1] = t;
 45     while(ql <= qr){
 46         int x = que[ql++];
 47         for(int i = h[x], v = e[i].v; i; i = e[i].nxt, v = e[i].v){
 48             if(!d[v]){
 49                 ++gap[d[v] = d[x] + 1], que[++qr] = v;
 50             }
 51         }
 52     }
 53 }
 54 
 55 int aug(int x, int s, int t, int mi)
 56 {
 57     if(x == t)return mi;
 58     int flow = 0;
 59     for(int &i = last[x], v = e[i].v; i; i = e[i].nxt, v = e[i].v){
 60         if(d[x] == d[v] + 1){
 61             int tmp = aug(v, s, t, min(mi, e[i].w));
 62             flow += tmp, mi -= tmp, e[i].w -= tmp, e[i ^ 1].w += tmp;
 63             if(!mi)return flow;
 64         }
 65     }
 66     if(!(--gap[d[x]]))d[s] = n + m + 4;
 67     ++gap[++d[x]], last[x] = h[x];
 68     return flow;
 69 }
 70 
 71 int maxflow(int s, int t)
 72 {
 73     init(s, t);
 74     int ret = aug(s, s, t, inf);
 75     while(d[s] <= n + m + 3)ret += aug(s, s, t, inf);
 76     return ret;
 77 }
 78 
 79 /*void addedge(int u,int v,int w) {
 80     e[++tot]=(edge){v,w,h[u]};
 81     h[u]=tot;
 82     e[++tot]=(edge){u,0,h[v]};
 83     h[v]=tot;
 84 }
 85 void init(int s,int t) {
 86     memset(gap,0,sizeof gap),memset(d,0,sizeof d),++gap[d[t]=1];
 87     for (int i=1;i<=n + m + 3;++i) last[i]=h[i];
 88     que[ql=qr=1]=t;
 89     while (ql<=qr) {
 90         int x=que[ql++];
 91         for (int i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v) if (!d[v]) ++gap[d[v]=d[x]+1],que[++qr]=v;
 92     }
 93 }
 94 int aug(int x,int s,int t,int mi) {
 95     if (x==t) return mi;
 96     int flow=0;
 97     for (int &i=last[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v) if (d[x]==d[v]+1) {
 98         int tmp=aug(v,s,t,min(mi,e[i].w));
 99         flow+=tmp,mi-=tmp,e[i].w-=tmp,e[i^1].w+=tmp;
100         if (!mi) return flow;
101     }
102     if (!(--gap[d[x]])) d[s]=n + m + 4;
103     ++gap[++d[x]],last[x]=h[x];
104     return flow;
105 }
106 int maxflow(int s,int t) {
107     init(s,t);
108     int ret=aug(s,s,t,inf);
109     while (d[s]<=n + m + 3) ret+=aug(s,s,t,inf);
110     return ret;
111 }*/
112 
113 int main()
114 {
115     while(scanf("%d%d%d", &n, &m, &k) != EOF){
116         //init(1, n+ m + 3);
117         tot = 1;
118         memset(h, 0, sizeof(h));
119         int s = 1, t = n + m + 3;
120         addedge(s, 2, k);
121         for(int i = 1; i <= n; i++){
122             addedge(s, 2 + i, 1);
123             addedge(2, 2 + i, 1);
124             int t;
125             scanf("%d", &t);
126             for(int j = 0, mon; j < t; j++){
127                 scanf("%d", &mon);
128                 addedge(2 + i, 2 + n + mon, 1);
129             }
130         }
131         for(int i = 1; i <= m; i++){
132             addedge(2 + n + i, t, 1);
133         }
134         printf("%d\n", maxflow(s, t));
135     }
136 
137     return 0;
138 }
View Code

 

D---Country Meow【最小球覆蓋】

題意:

三維空間中有$n$個點,現在要在空間中找一個點,使得他到這$n$個點最遠的距離最小。

思路:

就是一個最小球覆蓋的板子題。找的模擬退火的板子cf過不了了。

用的三分的板子直接就過了。

#include<iostream>
//#include<bits/stdc++.h>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<climits>
#include<map>
using namespace std;
typedef long long LL;
#define N 100010
#define pi 3.1415926535
#define inf 0x3f3f3f3f

const int maxn = 105;
const double eps = 1e-7;
typedef struct {double p[3];}point;
point a[maxn];
int n;
double cal(point now)
{
    double ans=0.0;
    for(int i=0;i<n;i++)
        ans=max(ans,sqrt((a[i].p[0]-now.p[0])*(a[i].p[0]-now.p[0])+(a[i].p[1]-now.p[1])*(a[i].p[1]-now.p[1])+(a[i].p[2]-now.p[2])*(a[i].p[2]-now.p[2])));
    return ans;
}
point del(point now,int cnt)
{
    if(cnt>=3)
        return now;
    double r=100000,l=-100000;
    double dr,dl;
    point tp1,tp2,ans1,ans2,ans;
    tp1=tp2=ans=now;
    while(r-l>eps)
    {
        dr=(2*r+l)/3;
        dl=(2*l+r)/3;
        tp1.p[cnt]=dl;
        tp2.p[cnt]=dr;
        ans1=del(tp1,cnt+1);
        ans2=del(tp2,cnt+1);
        if(cal(ans1)>cal(ans2))
        {
            l=dl;
            ans=ans1;
        }
        else
        {
            r=dr;
            ans=ans2;
        }
    }
    return ans;
}

int main()
{ // freopen("t.txt","r",stdin);
    //ios::sync_with_stdio(false);
    //double ans;
    while(~scanf("%d", &n))
    {
        for(int i=0; i<n; i++)
            //cin>>node[i].x>>node[i].y>>node[i].z;
            scanf("%lf%lf%lf",&a[i].p[0],&a[i].p[1],&a[i].p[2]);
        //minball(n);
        //cout<<ans<<endl;
        point ans;
        printf("%.7f\n",cal(del(ans, 0)));
    }
    return 0;
}
View Code

 

K---Kangaroo Puzzle

題意:

思路:

這題代碼可不能折疊啊。隊友太強了!

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include <bits/stdc++.h>
 4 
 5 using namespace std;
 6 const int MAX_N = 10;
 7 char c[4] = {'L', 'R', 'U', 'D'};
 8 
 9 int main()
10 {
11     int N, M;
12     cin >> N >> M;
13     string s;
14     for (int i = 1; i <= N; i++)
15         cin >> s;
16     int cnt = 0;
17     srand(56346275);
18     while (cnt++ < 50000) {
19         printf("%c", c[rand()%4]);
20     }
21     puts("");
22     return 0;
23 }

 

M---Mediocre String Problem【manacher】【exKMP】

題意:

有一個串$S$,一個串$T$。現在要在$S$中選一段$S[i,j]$,和$T$中的$T[1,k]$拼起來是一串回文。問有多少種不同的三元組$(i,j,k)$

思路:

詳細題解見:https://www.cnblogs.com/wyboooo/p/9982651.html

  1 #include<iostream>
  2 //#include<bits/stdc++.h>
  3 #include<cstdio>
  4 #include<cmath>
  5 //#include<cstdlib>
  6 #include<cstring>
  7 #include<algorithm>
  8 //#include<queue>
  9 #include<vector>
 10 //#include<set>
 11 //#include<climits>
 12 //#include<map>
 13 using namespace std;
 14 typedef long long LL;
 15 #define N 100010
 16 #define pi 3.1415926535
 17 #define inf 0x3f3f3f3f
 18 
 19 const int maxn = 1e6 + 5;
 20 char s[maxn], ss[maxn * 2], t[maxn], s_rev[maxn];
 21 LL pre[maxn * 2];
 22 int lens, lent, p[maxn * 2];
 23 
 24 int init()
 25 {
 26     ss[0] = '$';
 27     ss[1] = '#';
 28     int lenss = 2;
 29     for(int i = 0; i < lens; i++){
 30         ss[lenss++] = s[i];
 31         ss[lenss++] = '#';
 32     }
 33     ss[lenss] = '\0';
 34     return lenss;
 35 }
 36 
 37 void manacher()
 38 {
 39     int lenss = init();
 40     int id, mx = 0;
 41     for(int i = 1; i < lenss; i++){
 42         if(i < mx){
 43             p[i] = min(p[2 * id - i], mx - i);
 44         }
 45         else{
 46             p[i] = 1;
 47         }
 48         while(ss[i - p[i]] == ss[i + p[i]])p[i]++;
 49         if(mx < i + p[i]){
 50             id = i;
 51             mx = i + p[i];
 52         }
 53     }
 54 }
 55 
 56 int nxt[maxn],ex[maxn]; //ex數組即為extend數組
 57 //預處理計算next數組
 58 void GETNEXT(char *str)
 59 {
 60     int i=0,j,po,len=strlen(str);
 61     nxt[0]=len;//初始化next[0]
 62     while(str[i]==str[i+1]&&i+1<len)//計算next[1]
 63     i++;
 64     nxt[1]=i;
 65     po=1;//初始化po的位置
 66     for(i=2;i<len;i++)
 67     {
 68         if(nxt[i-po]+i<nxt[po]+po)//第一種情況,可以直接得到next[i]的值
 69         nxt[i]=nxt[i-po];
 70         else//第二種情況,要繼續匹配才能得到next[i]的值
 71         {
 72             j=nxt[po]+po-i;
 73             if(j<0)j=0;//如果i>po+nxt[po],則要從頭開始匹配
 74             while(i+j<len&&str[j]==str[j+i])//計算next[i]
 75             j++;
 76             nxt[i]=j;
 77             po=i;//更新po的位置
 78         }
 79     }
 80 }
 81 //計算extend數組
 82 void EXKMP(char *s1,char *s2)
 83 {
 84     int i=0,j,po,len=strlen(s1),l2=strlen(s2);
 85     GETNEXT(s2);//計算子串的next數組
 86     while(s1[i]==s2[i]&&i<l2&&i<len)//計算ex[0]
 87     i++;
 88     ex[0]=i;
 89     po=0;//初始化po的位置
 90     for(i=1;i<len;i++)
 91     {
 92         if(nxt[i-po]+i<ex[po]+po)//第一種情況,直接可以得到ex[i]的值
 93         ex[i]=nxt[i-po];
 94         else//第二種情況,要繼續匹配才能得到ex[i]的值
 95         {
 96             j=ex[po]+po-i;
 97             if(j<0)j=0;//如果i>ex[po]+po則要從頭開始匹配
 98             while(i+j<len&&j<l2&&s1[j+i]==s2[j])//計算ex[i]
 99             j++;
100             ex[i]=j;
101             po=i;//更新po的位置
102         }
103     }
104 }
105 
106 
107 int main()
108 {
109 
110     while(scanf("%s", s) != EOF){
111         scanf("%s", t);
112         lens = strlen(s);
113         lent = strlen(t);
114         for(int i = 0; i <= lens * 2 + 2; i++){
115             pre[i] = 0;
116             p[i] = 0;
117             ex[i] = 0;
118         }
119         manacher();
120         for(int i = lens * 2; i >= 2; i--){
121             int x = i / 2;
122             pre[x]++;
123             pre[x - (p[i] / 2)]--;
124         }
125         for(int i = lens; i >= 1; i--){
126             pre[i] += pre[i + 1];
127         }
128 
129         for(int i = 0; i <= lens; i++){
130             s_rev[i] = s[lens - 1 - i];
131         }
132         EXKMP(s_rev, t);
133         LL ans = 0;
134         /*for(int i = 1; i <= lens; i++){
135             cout<<pre[i]<<" "<<ex[i]<<endl;
136         }*/
137         for(int i = 1; i <= lens; i++){
138             //if(ex[lens - i + 1])
139             ans += 1LL * ex[lens - i + 1] * pre[i];
140         }
141         printf("%I64d\n", ans);
142     }
143     return 0;
144 }
View Code

 

 

J---Prime Game【數論】

題意:

給定$n$個數,$fra(i,j)$表示第$i$個數到第$j$個數相乘的值的不同質因子個數,求$\sum_{i=1}^{n} \sum_{j=i}^{n}fra(i,j)$

思路:

預處理把區間內的所有質數篩出來。

然后枚舉每個數的質因子,找到這個質因子對答案的貢獻。

每次都找到這個質因子上一次出現的位置,那么他對這段區間都是有貢獻的。

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 typedef long long ll;
 5 const int MAX_N = 1e6 + 5;
 6 
 7 int prime[MAX_N+1];
 8 void getPrime()
 9 {
10     memset(prime, 0, sizeof prime);
11     for (int i = 2; i <= MAX_N; i++) {
12         if (!prime[i]) prime[++prime[0]] = i;
13         for (int j = 1; j <= prime[0] && prime[j] <= MAX_N/i; j++) {
14             prime[i*prime[j]] = true;
15             if (i%prime[j] == 0)
16                 break;
17         }
18     }
19 }
20 
21 int a[MAX_N];
22 int pre[MAX_N];
23 int main()
24 {
25     getPrime();
26     int N;
27     cin >> N;
28     memset(pre, 0, sizeof pre);
29     for (int i = 1; i <= N; i++) {
30         scanf("%d", a+i);
31     }
32 
33     ll ans = 0;
34     for (int i = 1; i <= N; i++) {
35         for (int j = 1; prime[j] <= a[i]/prime[j]; j++) {
36             ll curp = prime[j];
37             if (a[i] % prime[j] == 0) {
38                 ll l = i - pre[curp];
39                 ll r = N-i+1;
40                 ans += l*r;
41                 pre[curp] = i;
42                 while (a[i]%curp == 0)
43                     a[i] /= curp;
44             }
45         }
46         if (a[i] > 1) {
47             ll curp = a[i];
48             ll l = i - pre[curp];
49             ll r = N-i+1;
50             ans += l*r;
51             pre[curp] = i;
52             while (a[i]%curp == 0)
53                 a[i] /= curp;
54         }
55     }
56     cout << ans << endl;
57     return 0;
58 }
59 /*
60 10
61 99 62 10 47 53 9 83 33 15 24
62 */
View Code

 

A---Adrien and Austin【博弈論】

題意:

有$n$個石頭並有編號,每次最多可以取$k$個最少取$1$個連續的石頭,兩個人輪流取,誰不能取了就輸了。

思路:

剛開始沒看到連續的,想半天都是錯的。

如果是連續的話,情況就比較簡單了。

先手對於任意的一段連續的$n$都可以把他取成大小相等的兩段。后手在任意一段取,先手都可以在另一段對稱的取。所以先手必勝。

當$n$是奇數,$k$是$1$的時候,先手沒辦法取出這樣的兩段。所以后手能贏。$n$為$0$后手也能贏。

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 const int MAX_N = 1e6 + 5;
 5 string s[2] = {"Adrien", "Austin"};
 6 
 7 
 8 int main()
 9 {
10     int N, K;
11     cin >> N >> K;
12     if (N == 0)
13         cout << s[1] << endl;
14     else if (K == 1 && N%2 == 0) {
15         cout << s[1] << endl;
16     }
17     else {
18         cout << s[0] << endl;
19     }
20     return 0;
21 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM