D | 【NOIP2017提高组 day1】小凯的疑惑 |
E | 【NOIP2017提高组 day1】时间复杂度 |
F | 【NOIP2017提高组 day1】逛公园 |
A.【NOIP2017提高组 day1】小凯的疑惑
看到这道题我就知道要凉,其实昨天改题瞄到过这题一眼,但只知道是图论,然后,,就没有然后了
考场上完全没有思路,于是自己yy了一个结论(没有一点理论依据的那种):大于a*b的数一定能被a,b表示出来。然后我就从大到小开始枚举,,,
没想到我的结论居然是对的!!!!然并卵,,
对于正解我只能%%%,证明过程看的我一脸懵逼,,,结果看到个打表证明公式的,,果然这才是我这种蒟蒻该看的题解。
不过看到个题解有句话说的挺像人话的(滑稽),虽然还是不知道为啥是对的,但还是搬下来了:
对于这道题的答案a * b - a - b 看到有一个比较易懂的解释:
假设两种钱每种最少要拿一次(也就是不能不拿),不能凑成的最小钱数为k,因为a和b互质,显然,k = a * b,(当k = a * b 时,由于ab互质,要么a拿b个,要么b拿a个)。
由于a和b可以一样都不拿,所以ans = k - a - b = a * b - a - b
代码真简洁,,,
#include<bits/stdc++.h>
using namespace std;
long long a,b;
int main() {
// freopen("math.in","r",stdin);
// freopen("math.out","w",stdout);
scanf("%lld%lld",&a,&b);
printf("%lld",(a-1)*b-a);
// printf("%lld",a*b-a-b);
return 0;
}
B. 【NOIP2017提高组 day1】时间复杂度
其实是挺简单一道模拟题(相比其他题真的好多了!!)不过我依然没打满
主要是考虑的情况太多了,考试时又没有大样例,搞得我bug也检查不出来,不过考完我自己去洛谷下了几个数据修改了无数次自己的代码后终于A了,(我代码bug真多)
#include<bits/stdc++.h>
using namespace std;
int t,st,flag,v[100100],cn[100100],po,give[100100],ans,fg,O;
int read() {
int sum=0;
char t=getchar();
while(t==' ')t=getchar();
if(t=='n')return 8848;
while(t>='0'&&t<='9') {
sum=sum*10+t-'0';
t=getchar();
}
return sum;
}
int readO() {
char t=getchar();
while(t!='(')t=getchar();
int sum=0;
t=getchar();
if(t=='1'){
t=getchar();return 8848;
}
t=getchar();
t=getchar();
while(t!=')') {
sum=sum*10+t-'0';
t=getchar();
}
return sum;
}
void init() {
flag=0;
st=0;
ans=0;
po=0;
fg=0;
memset(v,0,sizeof(v));
memset(cn,0,sizeof(cn));
memset(give,0,sizeof(give));
}
int main() {
// freopen("complexity.in","r",stdin);
// freopen("complexity.out","w",stdout);
scanf("%d",&t);
while(t--) {
int h;
init();
scanf("%d",&h);
O=readO();
for(int i=1; i<=h; i++) {
char a,b;
int x,y;
cin>>a;
if(a=='F') {
st++;
give[st]=0;
cin>>b;
x=read();
y=read();
if(v[int(b)]) {
flag=1;
continue;
}
v[int(b)]=1;
cn[st]=int(b);
if(fg)continue;
if(x>y) {
fg=st;
continue;
}
if(x<8848&&y==8848)give[st]=1;
ans=max(ans,po+=give[st]);
}
if(a=='E') {
v[cn[st]]=0;
po-=give[st];
if(fg==st)fg=0;
st--;
}
}
if(flag||st)printf("ERR");
else if((!ans&&O==8848)||(ans==O))printf("Yes");
else printf("No");
if(t)printf("\n");
}
return 0;
}
C. 【NOIP2017提高组 day1】逛公园
这次至少把暴力分都拿到了,,有进步,,,
正解感觉跟我思路差不多,不过加上了记忆化搜索和反向跑图这些骚操作,确实快了很多。
大致思路就是想spfa正向跑图求出每个点到1的最短路,然后反向跑图,每次答案存在f数组里,f[i][j]表示在i点还剩j点路程可以走的路线数,每次遍历如果当前状态的f数组的值已经求出来了就可以直接返回f数组的值了
#include<bits/stdc++.h>
using namespace std;
int t,n,m,k,p,ans,head[200100],idx,f[200100][55],v[200100],dis[200100],id,had[200100],vis[200010][55];
struct node {
int e,n,w;
} pr[200100],rp[200100];
void init() {
idx=0;id=0;ans=0;
memset(head,0,sizeof(head));
memset(had,0,sizeof(had));
memset(pr,0,sizeof(pr));
memset(v,0,sizeof(v));
memset(dis,0x7f,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(f,-1,sizeof(f));
memset(rp,0,sizeof(rp));
}
void add(int u,int v,int w) {
pr[++idx].e=v;
pr[idx].w=w;
pr[idx].n=head[u];
head[u]=idx;
rp[++id].e=u;
rp[id].w=w;
rp[id].n=had[v];
had[v]=id;
}
void spfa() {
queue<int>q;
q.push(1);
v[1]=1;
dis[1]=0;
while(!q.empty()) {
int x=q.front();
q.pop();
v[x]=0;
for(int i=head[x]; i; i=pr[i].n) {
int y=pr[i].e,z=pr[i].w;
if(dis[x]+z<dis[y]) {
dis[y]=dis[x]+z;
if(!v[y])v[y]=1,q.push(y);
}
}
}
}
int dfs(int x,int sum) {
int cnt=0;
if(sum<0||sum>k)return 0;
if(vis[x][sum]){vis[x][sum]=0;return -1;
}
if(f[x][sum]!=-1)return f[x][sum];
vis[x][sum]=1;
for(int i=had[x];i;i=rp[i].n){
int y=rp[i].e;
int val=dfs(y,dis[x]+sum-(dis[y]+rp[i].w));
if(val==-1){vis[x][sum]=0;return -1;
}
cnt=(cnt+val)%p;
}
vis[x][sum]=0;
if(x==1&&sum==0)cnt++;
f[x][sum]=cnt;
return cnt;
}
int main() {
// freopen("testdata (5).in","r",stdin);
// freopen("park.out","w",stdout);
scanf("%d",&t);
while(t--) {
init();
scanf("%d%d%d%d",&n,&m,&k,&p);
for(int i=1; i<=m; i++) {
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
spfa();
for(int i=0;i<=k;i++){
int val=dfs(n,i);
if(val==-1){ans=-1;break;
}
ans=(ans+val)%p;
}
printf("%d",ans);
if(t)printf("\n");
}
return 0;
}