2018-2019 ACM-ICPC, Asia Nanjing Regional Contest
文章目录
A.Adrien and Austin
题意:n个一排石子,两个人每轮可以取1-k个,问谁赢?
思路:模拟发现,只有k==1,n是奇数,后手才会赢,注意特判n == 0。
#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int mod=1e9+9;
int n,k;
int main()
{
cin>>n>>k;
if(n==1)
printf("Adrien\n");
else
{
if(k==1&&n%2==0||n==0)
printf("Austin\n");
else
printf("Adrien\n");
}
}
J.Prime Game
题意:题目说的很清楚了
思路:算每个素因子的贡献,存一下位置就行
#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int mod=1e9+9;
ll n,k;
vector<ll>G[maxn];
ll fac[maxn],a[maxn];
unordered_map<ll,bool>mp;
int main()
{
cin>>n;
for(ll i=1; i<=n; i++)
scanf("%lld",&a[i]);
for(ll i=1; i<=n; i++)
{
ll d=a[i];
for(ll j=2; j*j<=d; j++)
{
if(d%j==0)
{
G[j].push_back(i);
mp[j]=1;
while(d%j==0)
{
d/=j;
}
}
}
if(d>1)
G[d].push_back(i),mp[d]=1;
}
ll cnt=0;
for(auto it:mp)
{
fac[++cnt]=it.first;
}
ll ans=0;
for(ll i=1; i<=cnt; i++)
{
ll len=G[fac[i]].size();
ans=ans+(G[fac[i]][0]-0)*(n+1-G[fac[i]][0]);
for(ll j=1; j<len; j++)
{
ans=ans+(G[fac[i]][j]-G[fac[i]][j-1])*(n+1-G[fac[i]][j]);
}
}
printf("%lld\n",ans);
}
I.Magic Potion
题意:n个奥特曼打m个怪兽,每个奥特曼只能打自己对应的那几个怪兽,而且只能打死一个,你有K个特权,可以选择奥特曼再打一次,问你最后能杀死多少个怪兽?
思路:一看就是网络流,类似于最大匹配,我还想着拆点,不过拆点也能过,对答案没有影响,真是有点懵了,具体就是s—>奥特曼,s—>k个特权,k—>奥特曼,奥特曼—>怪兽—>t;
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=1e9+7;
const int maxn=1e4+5;
const int maxm=2e6+5;
int n,m,s,t,k,maxflow=0,lowflow;
struct node
{
int to,w,next;
} edge[maxm];
int head[maxn],cnt=1,dep[maxn],inque[maxn],cur[maxn];
void add(int u,int v,int w)
{
edge[++cnt].next=head[u];
edge[cnt].to=v;
edge[cnt].w=w;
head[u]=cnt;
}
struct Maxflow
{
bool bfs()
{
queue<int>q;
memset(dep,-1,sizeof dep);
dep[s]=0;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
inque[u]=0;
for(int i=head[u]; i; i=edge[i].next)
{
int v=edge[i].to,w=edge[i].w;
if(dep[v]==-1&&w)
{
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[t]!=-1;
}
int dfs(int u,int flow)
{
if(u==t)
{
return flow;
}
int rlow=0,used=0;
for(int i=cur[u]; i; i=edge[i].next)
{
int v=edge[i].to,w=edge[i].w;
if(w&&dep[v]==dep[u]+1)
{
if(rlow=dfs(v,min(flow,w)))
{
used+=rlow;
flow-=rlow;
edge[i].w-=rlow;
edge[i^1].w+=rlow;
if(flow==0)
break;
}
}
}
if(!used)
dep[u]=-1;
return used;
}
int Dinic()
{
maxflow=0;
while(bfs())
{
for(int i=1; i<=t; i++)
cur[i]=head[i];
maxflow+=dfs(s,INF);
}
return maxflow;
}
} ac;
int main()
{
scanf("%d%d%d",&n,&m,&k);
s=m+n*2+k+1,t=s+1;
for(int i=1; i<=n; i++)
{
int num,v;
scanf("%d",&num);
for(int j=1; j<=num; j++)
{
scanf("%d",&v);
add(i+n,v+n*2,1);
add(v+n*2,i+n,0);
}
add(s,i,1);
add(i,s,0);
}
for(int i=1; i<=k; i++)
{
add(s,i+n*2+m,1);
add(i+n*2+m,s,0);
for(int j=1; j<=n; j++)
{
add(i+n*2+m,j,1);
add(j,i+n*2+m,0);
}
}
for(int i=1; i<=m; i++)
{
add(i+n*2,t,1);
add(t,i+n*2,0);
}
for(int i=1;i<=n;i++)
{
add(i,i+n,2);
add(i+n,i,0);
}
int ans=ac.Dinic();
printf("%d\n",ans);
}
G.Pyramid(补)
题意:数等边三角形个数
思路:这种题推不出来公式,只能暴力打表找规律。学到了一种神奇的求多项式做差法,一直做差直到差值都为1,做差的次数就是项数,然后解方程求系数就行了。
#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int mod=1e9+7;
const double eps=1e-6;
ll n;
ll qpow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)
ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int main()
{
ll inv=qpow(24,mod-2);
int T;
cin>>T;
while(T--)
{
scanf("%lld",&n);
ll ans=qpow(n,4)+6*qpow(n,3)+11*qpow(n,2)+n*6;
ans=ans%mod;
ans=ans*inv;
ans=ans%mod;
printf("%lld\n",ans);
}
}
D.Country Meow
题意:给你一系列三维点,问你找一个中心位置,要求尽量缩小与这些点的距离。
思路:n=100,考虑三分x,y,z,三次三分就过了。
#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int mod=1e9+9;
const double eps=1e-6;
int n;
struct point
{
double x,y,z;
}p[maxn];
double dist(double x,double y,double z,point c)
{
return (x-c.x)*(x-c.x)+(y-c.y)*(y-c.y)+(z-c.z)*(z-c.z);
}
double judge(double x,double y,double z)
{
double dis=0;
for(int i=1;i<=n;i++)
{
dis=max(dis,dist(x,y,z,p[i]));
}
return dis;
}
double judge(double x,double y)
{
double l=-1e5,r=1e5;
while(r-l>eps)
{
double mid=(r-l)/3;
double Lmid=(l+mid),Rmid=r-mid;
if(judge(x,y,Lmid)<judge(x,y,Rmid))
r=Rmid;
else
l=Lmid;
}
return judge(x,y,l);
}
double judge(double x)
{
double l=-1e5,r=1e5;
while(r-l>eps)
{
double mid=(r-l)/3;
double Lmid=(l+mid),Rmid=r-mid;
if(judge(x,Lmid)<judge(x,Rmid))
r=Rmid;
else
l=Lmid;
}
return judge(x,l);
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z);
double l=-1e5,r=1e5;
while(r-l>eps)
{
double mid=(r-l)/3;
double Lmid=(l+mid),Rmid=r-mid;
if(judge(Lmid)<judge(Rmid))
r=Rmid;
else
l=Lmid;
}
printf("%.12f\n",sqrt(judge(l)));
}
M.Mediocre String Problem
题意:有一个串s 和一个串 t,现在让你从串s中截一个子串k和t中的一个前缀p,k和p连起来要是一个回文串,k要比p长。
思路:k比p长,前缀后缀是回文考虑EKMP,中间那一部分直接求满足以i为起点的回文串的数量,马拉车就行。
#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int mod=1e9+7;
const double eps=1e-6;
char s[maxn],t[maxn],ma[maxn*2];
int mp[maxn*2],nxt[maxn];
ll extend[maxn],R[maxn];
void manacher(char s[],int len)
{
int l=0;
ma[l++]='$';
ma[l++]='#';
for(int i=0; i<len; i++)
{
ma[l++]=s[i];
ma[l++]='#';
}
ma[l]='\0';
int mx=0;
int id=0;
for(int i=0; i<l; i++)
{
mp[i]=mx>i?min(mp[2*id-i],mx-i):1;
while(ma[i+mp[i]]==ma[i-mp[i]])
mp[i]++;
if(i+mp[i]>mx)
{
mx=i+mp[i];
id=i;
}
}
for(int i=2;i<len*2+2;i++)
{
if(mp[i]>2)
R[i/2]++,R[i/2+(mp[i]-1)/2]--;
}
R[0]=1;
for(int i=1;i<=len;i++)
R[i]+=R[i-1];
}
void pre_EKMP(char x[],int m,int nxt[])
{
nxt[0] = m;
int j = 0;
while( j+1 < m && x[j] == x[j+1] )
j++;
nxt[1] = j;
int k = 1;
for(int i=2; i<m; i++)
{
int p = nxt[k]+k-1;
int L = nxt[i-k];
if( i+L < p+1 )
nxt[i] = L;
else
{
j = max(0,p-i+1);
while( i+j < m && x[i+j] == x[j])
j++;
nxt[i] = j;
k = i;
}
}
}
void EKMP(char x[],int m,char y[],int n,int nxt[],ll extend[])
{
pre_EKMP(x,m,nxt);
int j = 0;
while(j < n && j < m && x[j] == y[j])
j++;
extend[0] = j;
int k = 0;
for(int i = 1; i < n; i++)
{
int p = extend[k]+k-1;
int L = nxt[i-k];
if(i+L < p+1)
extend[i] = L;
else
{
j = max(0,p-i+1);
while( i+j < n && j < m && y[i+j] == x[j] )
j++;
extend[i] = j;
k = i;
}
}
}
int main()
{
scanf("%s%s",s,t);
int lens=strlen(s),lent=strlen(t);
reverse(s,s+lens);
EKMP(t,lent,s,lens,nxt,extend);
manacher(s,lens);
ll ans=0;
for(int i=1; i<lens; i++)
ans=ans+extend[i]*R[i-1];
printf("%lld\n",ans);
}
K.Kangaroo Puzzle(补)
题意:题意:在 n * m 的平面上有若干个袋鼠和墙(1为袋鼠,0为墙),每次可以把所有袋鼠整体往一个方向移动一步(不能走出边界和不能走到墙),为在不超过5e4步的情况下能否把全部袋鼠聚集在同一个位置。
思路:先预处理每个袋鼠到其他袋鼠的初始方向,然后每次选两个不同的袋鼠,其中一个向另一个逼近,直到聚集在一起,然后重复该操作。玄学随机也很香
#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int mod=1e9+7;
const double eps=1e-6;
char s[maxn],t[maxn];
int n,m;
int main()
{
s[0]='L';
s[1]='R';
s[2]='U';
s[3]='D';
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
scanf("%s",t);
for(int i=1; i<=50000; i++)
{
printf("%c",s[rand()%4]);
}
}