Codeforces Round #536 (Div. 2)题解
http://codeforces.com/contest/1106
A. Lunar New Year and Cross Counting
解题思路
暴力对全图进行以此搜索即可
AC代码
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
char m[505][505];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%s",m[i]+1);
int cnt=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(m[i][j]=='X'&&m[i-1][j-1]=='X'&&m[i+1][j+1]=='X'&&m[i-1][j+1]=='X'&&m[i+1][j-1]=='X')
{
cnt++;
}
}
}
cout<<cnt<<endl;
}
B. Lunar New Year and Food Ordering
题目大意
简单模拟.有n个顾客,m种菜,每种菜有一定的价值和数量,顾客优先渴望一种菜品,如果缺乏这种菜品则会在现有的菜品中选择最便宜(如果有多个价格一样为最便宜的菜品,则选择尽可能便宜的)的给顾客,如果顾客不能吃够需求数量的菜就不会给钱,不论如何都会尽量给一个顾客菜吃,问最终获得的钱数
解题思路
依照题意模拟就行了模拟
AC代码
#include<iostream>
#include<queue>
#include<algorithm>
#define int long long
using namespace std;
struct node{
int c,id;
friend bool operator<(node x,node y)
{
if(x.c==y.c) return x.id<y.id;
else return x.c<y.c;
}
}C[100005];
int a[100005];
int cs[100005];
int32_t main()
{
int n,m;
long long tot=0;
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),tot+=a[i];
for(int i=1;i<=n;i++) scanf("%lld",&C[i].c),C[i].id=i,cs[i]=C[i].c;
sort(C+1,C+1+n);
int t,d;
int loc=1;
while(m--)
{
scanf("%lld%lld",&t,&d);
if(a[t]>=d)
{cout<<d*cs[t]<<endl;a[t]-=d;continue;}
else
{
int ans=0;
d-=a[t];
ans+=a[t]*cs[t];
a[t]=0;
while(d&&loc<=n)
{
if(d>a[C[loc].id])
{
d-=a[C[loc].id];
ans+=a[C[loc].id]*C[loc].c;
a[C[loc].id]=0;
loc++;
}
else
{
a[C[loc].id]-=d;
ans+=d*C[loc].c;
d=0;
}
}
if(d!=0) ans=0;
cout<<ans<<endl;
}
}
}
C. Lunar New Year and Number Division
题目大意
给出一些数,使其分为若干集合(每个集合最少两个数),要求集合中数和的平方的和最小
解题思路
集合自然是越小越好,因此尽量为2元集合,再者小大搭配
AC代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
int arr[300005];
int32_t main()
{
int n;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&arr[i]);
}
sort(arr+1,arr+1+n);
int sum=0;
for(int i=1;i<=n/2;i++)
{
sum+=(arr[i]+arr[n-i+1])*(arr[i]+arr[n-i+1]);
}
cout<<sum<<endl;
}
D. Lunar New Year and a Wander
题目大意
给出一幅连通图,对图进行遍历,已经走过的点可以重复走,问第一次走过点的字典序最小的点序列
解题思路
优先队列存入队的点优先搜索序号小的点.
AC代码
#include<cstdio>
#include<vector>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
vector<int> g[100005];
vector<int> ans;
bool vis[100005];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
memset(vis,0,sizeof(vis));
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
priority_queue<int, vector<int>, greater<int> > q;
q.push(1);
vis[1]=1;
while(!q.empty())
{
int x=q.top();
q.pop();
ans.push_back(x);
for(auto v:g[x])
{
if(!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
for(int i=0;i<ans.size();i++)
{
cout<<ans[i];
if(i==ans.size()-1) cout<<endl;
else cout<<' ';
}
}
E. Lunar New Year and Red Envelopes
题目大意
现在有一些红包其在s,t天之间可以被收取,若要收取这个红包则需要从开始收取的那一天一直到第d天才可以收下这个红包,收取后可以得到利益w.A按照一定的贪心策略收取红包:在一天中尽可能选择金额大的红包,如金额相同则选择d尽可能大的红包.现有一人可以有m天的机会来阻止A收取红包,即他可以选择m天使得这m天中A不能开始收取红包,问能使A收到最少金额红包的金额是多少
解题思路
对天数倒着DP,同时对天数和阻止次数DP即可
AC代码
#include<iostream>
#include<cstdio>
#include<set>
#include<algorithm>
#include<vector>
#define int long long
using namespace std;
typedef pair<int,int> pii;
const int size=1e5+5;
vector<pii> days[size];
vector<pii> dayt[size];
multiset<pii> ms;
int dp[size][205];
int32_t main()
{
int n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
int s,t,d,w;
for(int i=1;i<=k;i++)
{
scanf("%lld%lld%lld%lld",&s,&t,&d,&w);
days[s].push_back(pii(-w,-d));
dayt[t].push_back(pii(-w,-d));
}
for(int i=n;i>=1;i--)
{
for(auto t:dayt[i]) ms.insert(t);
for(int j=0;j<=m;j++)
{
if(!ms.empty()) dp[i][j]=dp[-(*ms.begin()).second+1][j]-((*ms.begin()).first);
else dp[i][j]=dp[i+1][j];
if(j) dp[i][j]=min(dp[i][j],dp[i+1][j-1]);
}
for(auto s:days[i]) ms.erase(ms.find(s));
}
cout<<dp[1][m]<<endl;
}
F. Lunar New Year and a Recursive Sequence
题目大意
给出一个序列,其前k-1项为1,其中对
i
>
k
i>k
i>k的项可以通过公式
f
i
=
(
∏
j
=
1
k
f
i
−
j
b
j
)
m
o
d
p
f_i=(\prod_{j=1}^{k}f^{b^j}_{i-j})mod\ p
fi=(j=1∏kfi−jbj)mod p
得出,现给出序列的第n项为m问第k项是否存在,若存在为几
解题思路
设第k项为x则原式可以化为求解 x q % p = m x^q\%p=m xq%p=m其中q可以通过矩阵快速幂得到.这时为了求解x我们需要用到原根的性质.当a为p的原根时函数 G ( b ) = a b m o d p G(b)=a^b mod\ p G(b)=abmod p有循环节 ϕ ( p ) \phi(p) ϕ(p)其中且循环节中的数都是不相同的因此总是有唯一对应的故要求解x仅需要求解p的原根的几次方求模为m再对这个原根乘方即为答案.其中998244353有大家都直到的原根"3"(反正我原来不知道)由此通过上述方法求解即可
AC代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long LL;
unordered_map<LL,LL> mp;
const int mod=998244353;
struct mat{
LL m[110][110];
mat(int x=0){memset(m,0,sizeof m);for(int i=1;i<110;i++)m[i][i]=x;}
friend mat operator*(mat a,mat b){
mat nxt;
for(int i=1;i<110;i++)for(int j=1;j<110;j++)for(int k=1;k<110;k++)
nxt.m[i][j]=(nxt.m[i][j]+a.m[i][k]*b.m[k][j])%(mod-1);
return nxt;
}
};
mat quick_pow_m(mat x,int b)
{
mat ans(1);
while(b)
{
if(b&1) ans=ans*x;
x=x*x;
b>>=1;
}
return ans;
}
int quick_pow(int a,int b,int p)
{
int ans=1;a%=p;
while(b)
{
if(b&1) ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
int BSGS(int B,int N,int P=mod)
{
mp.clear();
if(B%P==0) return -1;
LL ans=0;
LL m=ceil(sqrt(P));
for(int i=0;i<=m;i++)
{
if(!i) ans=N%P,mp[ans]=i;
else
{
ans=ans*B%P;
mp[ans]=i;
}
}
LL t=quick_pow(B,m,P);ans=1;
for(int i=1;i<=m;i++)
{
ans=(ans*t)%P;
if(mp[ans])
{
int s=i*m-mp[ans];
return (s%P+P)%P;
}
}
return -1;
}
int exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
y=0;
x=1;
return a;
}
int r=exgcd(b,a%b,x,y);
int temp=y;
y=x-(a/b)*y;
x=temp;
return r;
}
int32_t main()
{
int k;
mat s(0);
scanf("%lld",&k);
for(int i=1;i<k;i++) s.m[i][i+1]=1;
for(int i=1;i<=k;i++) scanf("%lld",&s.m[k][k+1-i]);
int n,fn;
scanf("%lld%lld",&n,&fn);
int temp=quick_pow_m(s,n-k).m[k][k];
int ans=BSGS(3,fn);
if(ans==-1) {cout<<-1<<endl;return 0;}
int x,y;
LL g=exgcd(temp,mod-1,x,y);
if(ans%g) {cout<<-1<<endl;return 0;}
int p=(ans/g*x%(mod-1)+mod-1)%(mod-1);
ans=quick_pow(3,p,mod);
cout<<ans<<endl;
}