头大
这个暑假完就要去搞NOIP了。。。
暑假55天也就20次测试。。。为防万一我还是每次测试玩都写个总结。。
终于在经过两周的煎熬后我终于能考好一次了。。莫名感动(可能是吧)
据曾老说这次给我们出题的是上一届的(noi还是noip?)金牌选手给我们出的,然后三个题的背景都不一样。。。【COCI AKCIJA,Codechef STDYTAB,CEOI BOARD】看着也是一脸懵逼。
不过这个分还是没上200,如果是110来计的话就能上270了还是不错,可以给原来成外的装个逼(hh)
Day2 (166/300)
T1 Bookstore(100/100)
题目背景
SOURCE:NOIP2016-RZZ-2 T1
题目描述
在遥远的汪星球,小W开了一家书店。
这家书店推出促销活动,即每次顾客可以选择三本书,然后可以免去其中最便宜的那本书的价格(也就是只需要支付最贵的两本书的价格之和的钱,就能买到这三本书)。当然,如果顾客选择的书不足三本,那么就只能付全部的价钱了。
现在,有一个顾客挑选了 n 本书,这个顾客想要知道,自己最少付多少钱可以买到全部的书?
输入格式
输入 n+1 行,第一行一个整数 n ,表示顾客要买的书的本书。
接下来 n 行,每行一个整数,表示每本书的价钱。
输出格式
输出一行,表示顾客最少要付多少钱。
样例数据 1
输入 [复制]
4
3
2
3
2
输出
8
备注
【数据规模与约定】
对于 50% 的数据,n≤2000。
对于 100% 的数据,1≤n≤100000,1≤ci≤100000。
智障暴力题。不过当时做完我还很不放心的回去检查了好几遍。。被T1坑得怕了。
每次比赛总有人被T1坑。
STD.CPP
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
using namespace std;
long long n=0,a[100005],ans=0;
int main()
{
freopen("bookstore.in","r",stdin);
freopen("bookstore.out","w",stdout);
memset(a,0,sizeof(a));
cin >> n;
for(int i=1;i<=n;i++)
{
cin >> a[i];
ans += a[i];
}
sort(a+1,a+n+1);
while(n-2>=0)
{
ans -= a[n-2];
n-=3;
}
cout << ans;
}
T2 Table(60/100)
题目背景
SOURCE:NOIP2016-RZZ-2 T2
题目描述
给定一个 n×m 的矩阵,行列均从 1 开始标号。
一个矩阵被认为是稳定的,当且仅当对于任意的 2≤i≤n,第 i 行的数的和不小于第 i−1 行的数的和,且最后一行的数的和小于等于 m ,并且要求矩阵中所有的元素都是非负的。
求所有 n×m 的稳定矩阵的方案数,答案对 109 取模。
输入格式
第一行一个整数 T ,表示数据组数。
每组数据一行两个整数 n,m 。
输出格式
输出 T 行,每行一个整数,表示方案数。
样例数据 1
输入 [复制]
3
1 1
2 2
2 3
输出
2
25
273
备注
【数据规模与约定】
对于 30% 的数据,n,m≤3。
对于 60% 的数据,n,m≤50。
对于 100% 的数据,1≤n,m≤2000;1≤T≤10。
MY.CPP
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
using namespace std;
int n,m,t,ans;
int dp1[2005][2005],dp2[2005][2005];
const int maxx = 1000000000;
// dp1[当前个数][当前总合]=方案数 dp2[当前行数][当前总合]=方案数
int main()
{
freopen("table.in","r",stdin);
freopen("table.out","w",stdout);
cin >> t;
while(t--)
{
memset(dp1,0,sizeof(dp1));
memset(dp2,0,sizeof(dp2));
cin >> n >> m;
for(int i=0;i<=m;i++)
dp1[1][i] = 1;
for(int i=2;i<=m;i++)
for(int j=0;j<=m;j++)
for(int k=0;k<=j;k++)
{
dp1[i][j] += dp1[i-1][j-k];
dp1[i][j] %= maxx;
}
for(int i=0;i<=m;i++)
dp2[1][i] = dp1[m][i];
for(int i=2;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=0;k<=j;k++)
{
dp2[i][j] += (dp2[i-1][k]*dp1[m][j]);
dp2[i][j] %= maxx;
}
for(int i=0;i<=m;i++)
{
ans += dp2[n][i];
ans %= maxx;
}
cout << ans << endl;
ans = 0;
}
}
头一次正确地使用了Dp。。两个Dp一个预处理一个递推答案,以n^3的时间复杂度过了60%而且事后只需要优化就能得标答也是把我也吓一跳。
Dp真是一个神奇的东西。
题解就是用前缀和等各种方法把n^3的时间复杂度降成了n^2。
STD.CPP
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<cstring>
#include<string>
#include<cctype>
using namespace std;
const int mod=1e9;
int n,m,T,dp[2001][2001],c[4002][4002];
//---------------------
inline int Readint()
{
int i=0,f=1; char ch;
for(ch=getchar();(ch<'0'||ch>'9');ch=getchar());
for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
return i*f;
}
//---------------------
int main()
{
//freopen("table.in","r",stdin);
//freopen("table.out","w",stdout);
for(int i=0;i<=4001;i++) c[i][0]=1,c[i][i]=1;
for(int i=1;i<=4001;i++)
for(int j=1;j<=i;j++){
c[i][j]=c[i-1][j-1]+c[i-1][j];
c[i][j]%=mod;
}
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
memset(dp,0,sizeof(dp));
for(int i=0;i<=m;i++) dp[0][i]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
long long now=dp[i-1][j];
now=(now*c[j+m-1][m-1])%mod;
if(j) dp[i][j]=dp[i][j-1]+now;
else dp[i][j]=now;
dp[i][j]=dp[i][j]%mod;
}
}
cout<<dp[n][m]<<endl;
}
return 0;
}
T3 Board(6/100)
题目背景
SOURCE:NOIP2016-RZZ-2 T3
题目描述
给出这样一棵“二叉树”:
每个节点有左右两个儿子,并如下定义每个节点的高度:假设父亲节点的高度为 h ,那么他的两个儿子的节点的高度都是 h + 1 ,相同高度的所有节点称作一层。
每个节点的左儿子的子树都在右儿子的子树的左边,每一层相邻的两个节点之间有一条边。
下面是一个例子:
每一条图上的路径用一个字符串表示,字符串中的每一个字符表示一个移动。字符仅包含如下五种:
1:表示移动到当前节点的左儿子
2:表示移动到当前节点的右儿子
U:表示移动到当前节点的父亲节点
L:表示移动到当前节点同层的左边的节点(保证当前节点在这一层中不是最左边的节点)
R:表示移动到当前节点同层的右边的节点(保证当前节点在这一层中不是最右边的节点)
用一条路径来表示这条路径的终点,例如路径:221LU 就表示上图中的节点 A 。
给出两条路径,你的任务是求出着两条路径的终点之间的最短路。
输入格式
输入两行,每行一个字符串,分别表示两条路径。
输出格式
输出一行一个整数,表示能得到的串的总数。
样例数据 1
输入 [复制]
221LU
12L2
输出
3
备注
【数据规模与约定】
用 D 表示所有经过的节点中,深度最大的节点的深度;S 表示输入字符串的最大长度。
对于 10% 的数据,D≤10。
对于 30% 的数据,D≤50。
对于 50% 的数据,D≤1000。
对于 70% 的数据,D≤20000。
对于 100% 的数据,D≤100000;S≤100000。
MY.CPP
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int n, s, cnt = 0;
int stack[200050], hash[70000], check[70000];
char c[5], num[20];
void cint(int aq,int cnt)
{
int ans=0,i;
for(i=1;i<=cnt;i++)
{
if(hash[stack[i]])
{
int h1 = stack[i];
int h2 = aq;
if((h1&h2)==stack[i])
ans += hash[stack[i]];
}
}
cout << ans << endl;
}
bool comp(int a,int b)
{
return a>b;
}
int init(int cnt,int flag)
{
sort(stack+1,stack+cnt+1,comp);
if(flag) cnt-=1;
for(int i=1;i<=cnt;i++)
check[stack[i]] = i;
return cnt;
}
int main()
{
int y=0;
cin >> n;
while(n--)
{
int flag=0;
scanf("%s",c); cin >> s;
if(c[0]=='a') {hash[s]+=1;if(hash[s]==1)stack[++cnt]=s;}
else if(c[0]=='d') {hash[s]-=1;if(hash[s]==0)stack[check[s]]=0,check[s]=0,flag = 1;}
else if(c[0]=='c') {cint(s,cnt);y+=1;}
cnt = init(cnt,flag);
}
}
一开始看见二叉树三个字我以为T3又是送。
看了半个小时后。。。坚决放弃。(我以为可以算出每个数的值结果深度可达100000连高精度都撑不住2^100000的巨大数字。)
别问我那3个点我是怎么过的。我也不知道。。orz
话说这次给的测试点真的多。。。50个。。中欧人这么有时间出数据?
STD’.CPP
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
const int Maxn=1e5+50;
int BITA[Maxn],BITB[Maxn],nowbitA=1,nowbitB=1,_;
char ch[Maxn];
inline void delA(int nowbit,int val)
{
if(val==1)
{
if(BITA[nowbit]==1)
{
BITA[nowbit]=0;
return;
}
else
{
BITA[nowbit]=1;
delA(nowbit-1,val);
}
}
else
{
if(BITA[nowbit]==0)
{
BITA[nowbit]=1;
return;
}
else
{
BITA[nowbit]=0;
delA(nowbit-1,val);
}
}
}
inline void delB(int nowbit,int val)
{
if(val==1)
{
if(BITB[nowbit]==1)
{
BITB[nowbit]=0;
return;
}
else
{
BITB[nowbit]=1;
delB(nowbit-1,val);
}
}
else
{
if(BITB[nowbit]==0)
{
BITB[nowbit]=1;
return;
}
else
{
BITB[nowbit]=0;
delB(nowbit-1,val);
}
}
}
inline int power(int x,int y)
{
int res=1;
while(y)
{
if(y&1)res*=x;
x*=x;
y>>=1;
}
return res;
}
int dis[Maxn*2],bg,maxd;
inline void calcA()
{
for(int i=2;i<=nowbitA;i++)
{
--bg;
if(BITA[i]==0){if(BITB[i]==1)dis[bg]++;if(!maxd)maxd=bg;}
else{if(BITB[i]==0)dis[bg]--;}
}
}
inline void calcB()
{
for(int i=2;i<=nowbitA;i++)
{
--bg;
if(BITB[i]==0){if(BITA[i]==1)dis[bg]++;if(!maxd)maxd=bg,_=1;}
else{if(BITA[i]==0)dis[bg]--;}
}
}
inline bool Isl()
{
for(int i=2;i<=nowbitA;i++)
{
if(BITA[i]==0&&BITB[i]==1)return true;
if(BITA[i]==1&&BITB[i]==0)return false;
}
}
inline int calcdis()
{
int t=0,bz=0;
for(int i=bg;i<=maxd;i++)
{
if(dis[i])t+=dis[i]*power(2,(i-bg));
}
return t;
}
inline bool judgesame()
{
if(nowbitA!=nowbitB)return false;
for(int i=1;i<=nowbitA;i++)if(BITA[i]^BITB[i])return false;
return true;
}
int main()
{
BITA[nowbitA]=BITB[nowbitB]=1;
scanf("%s",ch+1);
int len=strlen(ch+1);
for(int i=1;i<=len;i++)
{
if(ch[i]=='1')
{
nowbitA++;
BITA[nowbitA]=0;
}
else if(ch[i]=='2')
{
nowbitA++;
BITA[nowbitA]=1;
}
else if(ch[i]=='U'){nowbitA--;}
else if(ch[i]=='L'){delA(nowbitA,1);}
else delA(nowbitA,-1);
}
scanf("%s",ch+1);
len=strlen(ch+1);
for(int i=1;i<=len;i++)
{
if(ch[i]=='1')
{
nowbitB++;
BITB[nowbitB]=0;
}
else if(ch[i]=='2')
{
nowbitB++;
BITB[nowbitB]=1;
}
else if(ch[i]=='U'){nowbitB--;}
else if(ch[i]=='L'){delB(nowbitB,1);}
else delB(nowbitB,-1);
}
if(judgesame())cout<<0<<endl;
else
{
int ans=0;
if(nowbitA>nowbitB){ans+=(nowbitA-nowbitB);nowbitA=nowbitB;}
else {ans+=(nowbitB-nowbitA);nowbitB=nowbitA;}
bg=nowbitA;
if(Isl())calcA();
else calcB();
while(1)
{
if(maxd-bg>=25)
{
bg++,nowbitA--,nowbitB--;ans+=2;
}
else
{
int t1=calcdis();
bg++,nowbitA--,nowbitB--;
int t2=calcdis();
if(t1>=(t2+2))ans+=2;
else
{
ans+=t1;
break;
}
}
}
cout<<ans<<endl;
}
}
这次代码长的吓人。。还用了线段树也是够拼。
成功的脱了一次非入了一次欧orz。