【A】All X
【题目链接】点击打开链接
【解题思路】懂了(a/b)%mod = (a)%(b*mod)/b%mod,这个之后,这题完全就是水题。。。
【AC代码】
#include <queue>
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
#define ll long long
ll pow(ll a,ll n,ll mod){
ll res=1;
ll temp=a;
while(n){
if(n&1) res=res*temp%mod;
temp=((temp%mod)*(temp%mod))%mod;
n>>=1;
}
return res;
}
int main(){
int T;
ll x,m,k,c;
scanf("%d",&T);
for(int i=1; i<=T; i++){
scanf("%I64d%I64d%I64d%I64d",&x,&m,&k,&c);
ll ans=(pow(10,m,9*k)*x)%(9*k)-x;
printf("Case #%d:\n",i);
if(ans==9*c) puts("Yes");
else puts("No");
}
return 0;
}
【B】Sitting in Line
【题目链接】点击打开链接
【解题思路】简单状压dp,dp【i】【j】代表当前状态为i的时候,以a[j]结尾的a1*a2+a2*a3+a3*a4+...an-1*an的最大值。
【状态转移】dp[i|(1<<k)][k]=min(dp[i|(1<<k)][k],dp[i][j]+a[j]*a[k])
【AC代码】
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=16;
const int INF=0x3f3f3f3f;
int a[N],p[N],dp[1<<N][N];//状态为i,以a[j]结尾的最大的积
int n;
int main(){
int T,cas;
scanf("%d",&T);
for(cas=1; cas<=T; cas++){
scanf("%d",&n);
for(int i=0; i<n; i++){
scanf("%d%d",&a[i],&p[i]);
}
//INIT 1.
for(int i=0; i<(1<<n); i++)
for(int j=0; j<n; j++)
dp[i][j]=-INF;
//INIT 2.
for(int i=0; i<n; i++)
if(p[i]==0||p[i]==-1) //当p[i]==0或者p[i]==-1才可以初始化
dp[1<<i][i]=0;
//DP
for(int s=0; s<(1<<n); s++){
for(int i=0; i<n; i++){
if(dp[s][i]!=-INF){
for(int k=0; k<n; k++){//当是第p[k]个或者p[k]==-1的时候可以往里面放
if(((s&(1<<k))==0&&(p[k]==__builtin_popcount(s)||p[k]==-1)))
dp[s|(1<<k)][k]=max(dp[s|(1<<k)][k],dp[s][i]+a[i]*a[k]);
}
}
}
}
//get ans.
int ans=-INF;
for(int i=0; i<n; i++){
ans=max(ans,dp[(1<<n)-1][i]);
}
printf("Case #%d:\n",cas);
printf("%d\n",ans);
}
return 0;
}
【F】BD String.
【题目链接】点击打开链接
【解题思路】规律和递归!
从字符串规则可以看出以下几点
①S(n)的串长度为;
②S(n)中B的个数为个(以正中间的B为中心,假设左边有x个B,那么右边有个,即左边B的个数+右边B的个数=S(n-1)的串长度)
③远大于,故只是吓唬我们的
由上述几点可知,此题并没有表面上那么吓人
要求区间[l,r]内B的个数,我们可以转化为求[1,r]-[1,l-1]
而求区间[1,x]内B的个数,又可以如上述第②条所说,拆成3段,递归求解
比如x=12时,S=BBDBBDDBBBDD……
拆成3段如下:
右边4个是多余的,但是因为右边是由reverse+flip得到的,求右边4个中B的个数可以转化成求4-([1,7]中B的个数-[1,3]中B的个数)
而[1,3]中的个数又是得递归求解的
【AC代码】
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
ll l,r;
//2^n==x.
ll solve(ll x){
if(x==0) return 0;
ll m=0;
ll ans;
while(m<x) m=m*2+1;
m=(m-1)/2;
ans=(m+1)/2;
ll curlen=x-m-1;
return ans+1+curlen-(ans-solve(m-curlen));
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%I64d%I64d",&l,&r);
ll ans=solve(r)-solve(l-1);
printf("%I64d\n",ans);
}
return 0;
}
【G】 Gym Class
【题目链接】 点击打开链接
【解题思路】拓扑排序+贪心
众所周知,为了使每个人给的评分尽可能高,在没有条件的情况下,必定是ID高的排前面来得最优
但是度度熊没有那么善良
ID为B的同学不能排在A前面,好吧,怎么做呢?
对于这种调度问题,我们不妨用拓扑排序来求解
假设B不能排在A前面,那么我就在AB之间画一条从A指向B的边,那么我们可以得到一幅有向图
我们要按ID从大往小排,同时要考虑当前这位同学是否有特殊要求
上面有向图中,凡入度为0的结点,表示没有同学不希望该同学排在它的前面,故该同学可以按照ID从大往小排
所以此题的做法是,每次将入度为0的结点放入优先队列中,选取ID最大的结点先排,然后将该ID的同学讨厌的所有同学入度-1,若入度变为0同样要放入优先队列判断ID先后,这里建图可以邻接表实现,也可以利用一些vector
【AC代码】
#include <queue>
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
#define ll long long
const int inf=0x3f3f3f3f;
struct cmp{
bool operator()(int &a,int &b){
return a<b;
}
};
int n,m;
int in[100005];//记录入度
priority_queue<int,vector<int>,cmp>pq;//最大值优先
vector<int>v[100005];//存图
int main(){
int T;
int x,y;
scanf("%d",&T);
while(T--){
ll ans=0;
int minid=inf;
scanf("%d%d",&n,&m);
memset(in,0,sizeof(in));
for(int i=1; i<=n; i++) v[i].clear();
while(!pq.empty()) pq.pop();
for(int i=0; i<m; i++){
scanf("%d%d",&x,&y);
in[y]++;
v[x].push_back(y);
}
for(int i=1; i<=n; i++){
if(!in[i]) pq.push(i);
}
while(!pq.empty()){
int temp=pq.top();
pq.pop();
minid=min(minid,temp);
ans+=minid;
for(int i=0; i<v[temp].size(); i++){
in[v[temp][i]]--;
if(in[v[temp][i]]==0) pq.push(v[temp][i]);
}
}
printf("%lld\n",ans);
}
return 0;
}