CSP-S 2020 题解
UPD:T4题解来了
UPD2:T4过了 小细节小细节(指调了两天
UPD3:初中生能去noip啦 而且擦线省一
怎么说呢 这个结果 也就这样了吧 下次加油
怎么说呢 自己还是不太行 明明有370的傻逼分的但是还是挂掉了 现在就是个省二彩笔
算了 就这样吧 反正初中也去不了noip
T1
按照题意模拟即可,考场上脑瘫了哈哈哈没有对拍哈哈哈就忘记了模出来可能会有0这回事哈哈哈出题人nmsl
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int norday[12]={31,28,31,30,31,30,31,31,30,31,30,31};
const int runday[12]={31,29,31,30,31,30,31,31,30,31,30,31};
const int year4=366+365+365+365;
const int run4[4]={366,365,365,365};
const int run41[4]={365,365,365,366};
const int year400=146097;
bool pdrun(int y){
if(y<0){
return y%4==-1;
}
else if(y<=1582){
return y%4==0;
}
else {
return ((y%400==0)||(y%4==0&&y%100!=0));
}
}
int ansy,ansm,ansd;
void getdate(int y,int d){
if(pdrun(y)){
int tmp=0;
for(int i=0;i<12;++i){
tmp+=runday[i];
if(tmp>=d){
tmp-=runday[i];
ansd=d-tmp;
ansm=i+1;
return;
}
}
}
else {
int tmp=0;
for(int i=0;i<12;++i){
tmp+=norday[i];
if(tmp>=d){
tmp-=norday[i];
ansd=d-tmp;
ansm=i+1;
return;
}
}
}
}
signed main(){
int Q;
scanf("%lld",&Q);
while(Q--){
int n;
scanf("%lld",&n);
n++;
ansy=-4713;
if(n<365){
getdate(-4713,n);
printf("%lld %lld %lld BC\n",ansd,ansm,abs(ansy));
}
else if(n<=1721424){
ansy+=n/year4*4;
int tmp=n%year4;
//cout<<tmp<<endl;
if(tmp==0)ansy-=4,tmp=year4;
int sb=0;
for(int i=0;i<4;++i){
sb+=run4[i];
if(sb>=tmp){
ansy+=i;
sb-=run4[i];
sb=tmp-sb;
getdate(ansy,sb);
printf("%lld %lld %lld BC\n",ansd,ansm,abs(ansy));
break;
}
}
}
else {
n-=1721424;
ansy=0;
if(n<=577737){
ansy+=n/year4*4;
int tmp=n%year4;
if(tmp==0)ansy-=4,tmp=year4;
int sb=0;
for(int i=0;i<4;++i){
sb+=run41[i];
if(sb>=tmp){
ansy+=i+1;
sb-=run41[i];
sb=tmp-sb;
getdate(ansy,sb);
printf("%lld %lld %lld\n",ansd,ansm,abs(ansy));
break;
}
}
}
else {
n-=577737;
if(n<=78){
n+=287;
getdate(1582,n);
ansy=1582;
printf("%lld %lld %lld\n",ansd,ansm,abs(ansy));
}
else {
n-=78;
ansy=1582;
ansy+=n/year400*400;
int tmp=n%year400;
if(tmp==0)ansy-=400,tmp=year400;
int sb=0;
for(int i=1583;i<=1583+400-1;++i){
sb+=365+(((i%400==0)||(i%4==0&&i%100!=0))?1:0);
if(sb>=tmp){
ansy+=i-1582;
sb-=365+(((i%400==0)||(i%4==0&&i%100!=0))?1:0);
sb=tmp-sb;
getdate(ansy,sb);
printf("%lld %lld %lld\n",ansd,ansm,abs(ansy));
break;
}
}
}
}
}
}
}
/*
8
365
366
1721423
1721424
2299160
2299161
2299238
2299239
*/
T2
显然不用去管饲料是个啥,只需要判断某一位能不能用就可以了(qi互不相同),那么最后\(2^{可以用的位数}-已经有的个数\)即可
注意判k=64和n=0
#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
int n,m,c,k;
int alla;
bool tag[1000010];
int sum;
signed main(){
scanf("%llu%llu%llu%llu",&n,&m,&c,&k);
for(int i=1;i<=n;++i){
int ai;
scanf("%llu",&ai);
alla|=ai;
}
for(int i=1;i<=m;++i){
int p,q;
scanf("%llu%llu",&p,&q);
if(!((1ull<<p)&alla)){
if(!tag[p])sum++;
tag[p]=true;
}
}
if(k-sum==64){
if(n==0)puts("18446744073709551616");
else printf("%llu\n",(1ull<<63)-n+(1ull<<63));
}
else {
printf("%llu\n",(1ull<<(k-sum))-n);
}
}
T3
考虑倒着处理,那么就可以把原来的函数序列转换成每个函数分别需要单独执行的次数,也就是系数(这一步也要拓扑序以得到每个函数会把前面的函数乘上几倍)
那么每个函数再用拓扑序一个个往下更新,更新到底层的每个加法函数要执行几次,最后再执行就好了
两次的图刚好是反图
考场上分明都想出来了却不敢写...唉
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
int n;
int a[1000010];
int m;
struct func{
int type;
int x,v;
}q[1000010];
int tval[1000010];
struct qwq{
int v;
int nxt;
}edge[1000010];
int cnt=-1;
int head[1000010];
struct ed{
int u,v;
}e[1000010];
void add(int u,int v){
edge[++cnt].nxt=head[u];
edge[cnt].v=v;
head[u]=cnt;
}
int ind[1000010];
void topo_solve1(){
queue<int> qu;
for(int i=1;i<=m;++i){
if(!ind[i])qu.push(i);
}
while(!qu.empty()){
int u=qu.front();
qu.pop();
for(int i=head[u];~i;i=edge[i].nxt){
int v=edge[i].v;
tval[v]=(tval[v]*tval[u])%mod;
ind[v]--;
if(!ind[v]){
qu.push(v);
}
}
}
}
int tot;
int Q;
int que[1000010];
int allt[1000010];
int rea[1000010];
void topo_solve2(){
queue<int> qu;
for(int i=1;i<=m;++i){
if(!ind[i]){
qu.push(i);
}
rea[i]=allt[i];
}
while(!qu.empty()){
int u=qu.front();
qu.pop();
int nowk=rea[u];
for(int i=head[u];~i;i=edge[i].nxt){
int v=edge[i].v;
rea[v]=(rea[v]+nowk)%mod;
ind[v]--;
if(!ind[v]){
qu.push(v);
}
nowk=(nowk*tval[v])%mod;
}
}
}
signed main(){
memset(head,-1,sizeof(head));
scanf("%lld",&n);
for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
scanf("%lld",&m);
for(int i=1;i<=m;++i){
scanf("%lld",&q[i].type);
if(q[i].type==1){
scanf("%lld%lld",&q[i].x,&q[i].v);
tval[i]=1;
}
else if(q[i].type==2){
scanf("%lld",&q[i].v);
tval[i]=q[i].v;
}
else {
int cj;
scanf("%lld",&cj);
ind[i]=cj;
for(int j=1;j<=cj;++j){
int x;
scanf("%lld",&x);
add(x,i);
e[cnt].v=x,e[cnt].u=i;
}
tval[i]=1;
}
}
topo_solve1();
scanf("%lld",&Q);
for(int i=1;i<=Q;++i)scanf("%lld",&que[i]);
int k=1;
allt[que[Q]]=(allt[que[Q]]+k)%mod;
for(int i=Q-1;i>=1;--i){
k=k*tval[que[i+1]]%mod;
allt[que[i]]=(allt[que[i]]+k)%mod;
}
k=(k*tval[que[1]])%mod;
memset(head,-1,sizeof(head));
memset(edge,0,sizeof(edge));
int tot=cnt;cnt=-1;
memset(ind,0,sizeof(ind));
for(int i=0;i<=tot;++i){
add(e[i].u,e[i].v);
ind[e[i].v]++;
}
topo_solve2();
for(int i=1;i<=n;++i){
a[i]=(a[i]*k)%mod;
}
for(int i=1;i<=m;++i){
if(q[i].type==1){
a[q[i].x]=(a[q[i].x]+(rea[i]*q[i].v)%mod)%mod;
}
}
for(int i=1;i<=n;++i){
printf("%lld ",a[i]);
}
puts("");
}
/*
3
1 2 3
3
1 1 1
2 2
3 2 1 2
2
2 3
*/
T4
我这个做法可能会比较繁琐 请耐心看完w
首先显然如果每条蛇都无脑地吃下去的话 每一轮吃的蛇是固定的。
然后考虑得到这个每一轮吃的蛇的序列后怎么求出答案,显然我们从后往前扫,如果当前操作的吃蛇的蛇在后面会被吃,那么它就会在这一轮停下来,这一轮之后的所有吃的操作都失效,那么只要这样处理到第一轮,也就是找到最早的可能停下来的那一轮就能得到答案了。
那么考虑怎么得到这个序列。
设当前序列为a1,...,an且有序排列(题目给的肯定是有序的),分两种情况来讨论:
第一种,an-a1>=a1
那么我们可以发现,这一轮吃完后,最大值一定是不升的(an->an-1或an->an-a1),最小值一定是不降的(a1->a2或a1->an-a1),那么下一轮得到的蛇的长度一定会小于等于这一轮的蛇的长度,也就是说当满足an-a1>=a1时,每一轮的结果都满足单调性。所以我们可以开两个队列,第一个队列是原本的数组,第二个队列用于存放每一轮得出的蛇的长度,然后每次从第一个和第二个的队尾得到最大值,第一个和第二个的队头得到最小值,不断把当前轮的新蛇的长度放到第二个队列的头部就可以了。这样做显然是O(n)的。
第二种,an-a1<a1
这个时候就不满足上面的单调性了,我们要考虑新的做法。
先把第一种得到的两个有序队列合并为新的数组a1,...,am,并设k是满足ak=a1的最大值,也就是a1=a2=...=ak。
然后我们考虑一下在ak+1~am还没有每一条都吃过的时候的每一轮的新蛇的长度(这里只是长度,只有值满足,并不一定就是那个下标的蛇):
第一轮,am-a1,显然小于a1;第二轮,am-1-(am-a1)=am-1-am+a1,小于等于a1;第三轮,am-2-am-1+am-a1 ,是小于a1的。
于是我们发现,每一轮的最小值,要么是从a1~ak中没有吃的最小的蛇中取得,要么就是上一轮得到的那条新蛇。
于是我们用一个指针,指向一下当前轮的最小值应该从上一轮那条新蛇中取,还是从a1~ak中没有吃的最小的蛇中取,就可以得到最小值了。
我们又发现最大值会分别是am~ak+1,那么我们这里再用一个指针就可以了。
于是这一个部分也O(n)解决了。
接下来,ak+1~am每一条都至少吃过一条蛇了。根据上面的部分,我们现在剩的蛇要么全都是相等的,值都为a1;要么是一堆a1和一个小于a1的值。
对于第一种情况,将会是最大值吃最小值,然后得到0,然后被次大值吃,次大值的值不变,然后次大值吃次小值...如此递推。
对于第二种情况,要判断当前最大值吃完最小值后会不会是0,如果是0的话,下一轮吃他后最大值仍然不变;否则,吃他的最大值就会变成最小值。用一个指针判断一下就可以了。
这样也是O(n)的。
于是,这题就O(n)解决了。
code:
#include<bits/stdc++.h>
using namespace std;
int n;
int a[2000010];
int cnt;
struct node{
int val;
int num;
};
node tag[2000010];
int head[2],tail[2];
node q[2][2000010];
node minnode(node a,node b){
if(a.val==b.val?a.num>b.num:a.val>b.val)return b;
else return a;
}
node getmin(){
if(head[1]<=tail[1])return minnode(q[0][head[0]],q[1][head[1]]);
return q[0][head[0]];
}
node maxnode(node a,node b){
if(a.val==b.val?a.num>b.num:a.val>b.val)return a;
else return b;
}
node getmax(){
if(head[1]<=tail[1])return maxnode(q[0][tail[0]],q[1][tail[1]]);
return q[0][tail[0]];
}
int tot;
node newa[2000010];
int top;
node sta[2000010];
int lpos,rpos;
int vis[2000010];
void solve(){
memset(q,0,sizeof(q));
for(int i=1;i<=n;++i){
q[0][i].num=i,q[0][i].val=a[i];
}
cnt=0;
memset(tag,0,sizeof(tag));
head[0]=1,tail[0]=n;
head[1]=n+1,tail[1]=n;
node minn=q[0][1],maxn=q[0][n];
while(minn.val*2<=maxn.val&&tail[0]-head[0]+1+tail[1]-head[1]+1>=2){
node newnode;
bool pdmax=maxn.val==q[1][tail[1]].val&&maxn.num==q[1][tail[1]].num,
pdmin=minn.val==q[1][head[1]].val&&minn.num==q[1][head[1]].num;
--tail[pdmax];
++head[pdmin];
newnode.num=maxn.num;
newnode.val=maxn.val-minn.val;
q[1][--head[1]]=newnode;
tag[++cnt].num=minn.num;
tag[cnt].val=maxn.num;
minn=getmin(),maxn=getmax();
}
tot=0;
int i=head[0],j=head[1];
for(;i<=tail[0]&&j<=tail[1];++i){
maxn=maxnode(q[0][i],q[1][j]);
while(maxn.num==q[0][i].num&&maxn.val==q[0][i].val&&j<=tail[1]){
newa[++tot].val=q[1][j].val,newa[tot].num=q[1][j].num;
j++;
maxn=maxnode(q[0][i],q[1][j]);
}
newa[++tot].val=q[0][i].val,newa[tot].num=q[0][i].num;
}
while(i<=tail[0]){
newa[++tot].val=q[0][i].val,newa[tot].num=q[0][i].num;
i++;
}
while(j<=tail[1]){
newa[++tot].val=q[1][j].val,newa[tot].num=q[1][j].num;
j++;
}
int k=1;
while(newa[1].val==newa[k+1].val&&k<tot)k++;
lpos=1,rpos=tot;
int pos=lpos+1;
bool pd=0;
top=0;
for(;rpos>k;--rpos){
node newnode;
if(!pd){
newnode.val=newa[rpos].val-newa[lpos].val;
newnode.num=newa[rpos].num;
tag[++cnt].num=newa[lpos].num;
tag[cnt].val=newa[rpos].num;
bool ck=false;
if(newnode.val<newa[1].val||(newnode.val==newa[pos].val&&newnode.num<newa[pos].num)){
newa[0].val=newnode.val,newa[0].num=newnode.num;
lpos=0;
}
else {
sta[++top].val=newnode.val,sta[top].num=newnode.num;
lpos=pos;
pos=pos+1;
}
if(lpos==k+1){
pd=true;
lpos=top;
}
}
else {
if(lpos){
newnode.val=newa[rpos].val-sta[lpos].val;
newnode.num=newa[rpos].num;
tag[++cnt].num=sta[lpos].num;
tag[cnt].val=newa[rpos].num;
top--;
}
else {
newnode.val=newa[rpos].val-newa[lpos].val;
newnode.num=newa[rpos].num;
tag[++cnt].num=newa[lpos].num;
tag[cnt].val=newa[rpos].num;
}
if(newnode.val<newa[1].val){
newa[0].val=newnode.val,newa[0].num=newnode.num;
lpos=0;
}
else {
sta[++top].val=newnode.val,sta[top].num=newnode.num;
lpos=top;
}
}
}
while(k>=pos)sta[++top]=newa[k],k--;
if(lpos==0)sta[++top]=newa[lpos];
if(top>1){
int newr=1;
while(newr<top){
node maxn=sta[newr],minn=sta[top];
node newnode;
newnode.val=maxn.val-minn.val;
newnode.num=maxn.num;
tag[++cnt].num=minn.num,tag[cnt].val=maxn.num;
top--;
if(maxn.val!=newnode.val){
newr++;
sta[++top]=newnode;
}
}
}
memset(vis,0,sizeof(vis));
int no=n;
for(int i=cnt;i>=1;--i){
++vis[tag[i].num];
if(vis[tag[i].val]){
while(no>i)no--,--vis[tag[no].num];
}
}
printf("%d\n",n-no+1);
}
int main(){
int T;
scanf("%d",&T);
T--;
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
solve();
while(T--){
int m;
scanf("%d",&m);
for(int i=1;i<=m;++i){
int x;
scanf("%d",&x);
scanf("%d",&a[x]);
}
solve();
}
}