下笔春蚕食叶声。

2020 CSP-S2 题解+吐槽

作为一个无敌大傻逼,我 在考场上写了1.5h的T1 并且没开ll 没判1月0日。我 T3 T4 写了乱七八糟的暴力 加起来竟然只有40分。
保灵人来写题解了。/kk
T1 模拟大师。
(考场代码修改而来 有许多中文字符在机房的ubuntu下爆炸了)
upd:为什么ccf官方数据下洛谷的ac代码只有80了。。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL q;
LL gb=2299160,ans; //������ -4713.1.1 �� 2299160 ��jl���� 1582.10.4 
LL mt[15]={0,31,28,31,30,31,30,31,31,30,31,30,31};
LL nf(LL y){
	if(y<=1582){
		if(y<0) y++;
		if(y%4==0) return 366;
		else return 365;
	} else {
		if(y%4==0&&y%100!=0||y%400==0) return 366;
		else return 365;
	}
}
void js(LL y,LL num){//?
	for(LL i=1;i<=12;i++){
		LL nw=mt[i];if(nf(y)==366&&i==2) nw++;
		if(num-nw<=0){
			printf("%lld %lld %lld\n",num,i,y);//Day Month Year
			return;
		} else num-=nw;
	}
	return;
}
void js2(LL y,LL num){//?
	for(LL i=1;i<=12;i++){
		LL nw=mt[i];if(nf(y)==366&&i==2) nw++;
		if(num-nw<=0){
			if(y>=0) printf("%lld %lld %lld\n",num,i,y);
			else printf("%lld %lld %lld BC\n",num,i,-y);//Day Month Year
			return;
		} else num-=nw;
	}
	return;
}
void calc(LL num){//1582.10.5(����)֮����jl�� 
	//1582.1.1(����)֮����jl�� 
	num+=277;//????
	//400 years a group
	LL gsb=400*365+1+100-4;//1.1.1-400.12.31
	if(num<=365){//1582�� 
		js(1582,num);return;
	} else num-=365;
	if(num<=365){//1583
		js(1583,num);return;
	} else num-=365;
	if(num<=366){//1584
		js(1584,num);return;
	} else num-=366;
	//1585.1.1(����)֮����num�� 
	//1585 start 
	LL y=num/gsb; num-=y*gsb; y*=400; y+=1585;//before years
	for(LL i=0;i<=400;i++){
		LL jn=nf(y+i);
		if(num<=jn){
			js(y+i,num);return;
		}
		else num-=jn;
	}
	return;
}
void calc2(LL num){//-4713.1.1��ʼ��������num�� ����1.1
	//400 years a group ��֤��jl�� 
	LL gsb=400*365+100;//-.1.1-400.12.31
	if(num<=366){
		js2(-4713,num);return;
	} else num-=366;
	//-4712 start  
	LL y=num/gsb; num-=y*gsb; y*=400; y-=4712;//before years
	// û��0�� ��
	LL fl=0;
        if(num==0) y--,num=nf(y);
        if(y==0) y--,num=nf(y);
	for(LL i=0;i<=400;i++){
		if(y+i>=0&&!fl) y++,fl=1;
		LL jn=nf(y+i);
		if(num<=jn){ js2(y+i,num);return; }
		else num-=jn;
	}
	return;
}
void solve(LL jl){
	if(jl>gb){//>1582.10.4
		jl-=gb;jl+=10;//1582.10.5-10.14 not have
		calc(jl);//1582.10.5(����)֮����jl�� 
	} else {
		jl++; 
		calc2(jl);//-4713.1.1��ʼ������ ����1.1 
	} 
	return;
}
int main(){
	//freopen("P7075_7.in","r",stdin);
	//freopen("mine.out","w",stdout);
	scanf("%lld",&q);
	while(q--){
		LL r;scanf("%lld",&r);
		solve(r);
	}
	return 0;
}
/*
final
*/

T2 水题

震惊 我这个蒟蒻居然没挂分。。。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;;
int n,m,c,k,cnt=0;
LL res=0;
vector<int>vec[75];
bool sl[100000010];
unsigned long long ans=0;
int main(){
	//freopen("zoo.in","r",stdin);
	//freopen("zoo.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&c,&k);
	for(int i=1;i<=n;i++){
		unsigned long long a;scanf("%llu",&a); ans|=a;
	}
	for(int i=1;i<=m;i++){
		int p,q;
		scanf("%d%d",&p,&q);
		vec[p].push_back(q);
	}
	for(int i=0;i<k;i++){
		//��������
		int nw=k-1-i;
		if(ans&(1ull<<nw)){
			for(int j=0;j<vec[nw].size();j++) sl[vec[nw][j]]=1;
		}
	}
	for(int i=0;i<k;i++){
		bool fl=1;
		for(int j=0;j<vec[i].size();j++){
			if(!sl[vec[i][j]]) fl=0;
		//	cout<<i<<" "<<j<<" "<<vec[i][j]<<endl;
		}
		if(fl) cnt++;//possible
	}
	if(cnt<63){
		res=1ll<<cnt;
		printf("%lld\n",res-n);
	} else {
		if(n==0&&cnt==64) printf("18446744073709551616\n");
		else {
			unsigned long long tmp=(1ull<<(cnt-1));
			cout<<tmp*2-n<<endl;
		}
	}
	return 0;
}
/*
��|�������еĶ��o(n)
Ȼ��o(k)����Ҫ��Щ���ϡ�
ÿ��λ�� ���㵱ǰ�������Ƿ������㣿o(k)  
Ȼ���ټ����ж��ٶ����������㡣��1��λ�ÿ�����0��1 0��λ��ֻ����0�� 
Ҫunsigned long long �𣿣� 
*/

T3

考场上开T3T4的时候心里只有一个年头:暴力。

于是无脑暴力+逆元,50pts。

出考场听到是拓扑的那一刻真的要崩溃了,是啊 拓扑 多傻逼啊,我更傻逼。

先记忆化搜索预处理一下每个函数的后缀答案,然后拓扑排序统计一下就行。。。注意要倒着扫。

我 傻逼。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,E=1e6+10,mod=998244353;
int n,m,a[N],deg[N],t[N],id[N],val[N],c[N],f[N];
bool vis[N];
vector<int>vec[N],pre[N];
void add(int a,int b){ deg[b]++; vec[a].push_back(b); }
void init(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%d",&t[i]);
        if(t[i]==1){ scanf("%d%d",&id[i],&val[i]);continue; }
        if(t[i]==2){ scanf("%d",&val[i]); continue; } 
        scanf("%d",&c[i]);
        for(int j=1,x;j<=c[i];j++){ scanf("%d",&x); add(i,x); }
    }
    t[++m]=3; scanf("%d",&c[m]); f[m]=1;
    for(int i=1,x;i<=c[m];i++){ scanf("%d",&x); add(m,x); }
}
void dfs(int u){
    vis[u]=1;
    if(t[u]==1){ pre[u].push_back(1); return; }
    if(t[u]==2){ pre[u].push_back(val[u]); return; }
    int sz=vec[u].size();
    if(!sz){ pre[u].push_back(0); return; }
    pre[u].resize(sz);
    for(int i=0;i<sz;i++){
        int v=vec[u][i]; if(!vis[v]) dfs(v);
        pre[u][i]=pre[v][0];
    }
    for(int i=sz-2;i>=0;i--)
        pre[u][i]=1ll*pre[u][i+1]*pre[u][i]%mod;
    return;
}
void topo(){
    queue<int>q;
    for(int i=1;i<=m;i++)
        if(!deg[i]) q.push(i);
    while(!q.empty()){
        int u=q.front(); q.pop();
        int sz=vec[u].size();
        for(int i=0;i<sz;i++){
            int v=vec[u][i];
            deg[v]--; if(!deg[v]) q.push(v);
            if(!vis[u]) continue;
            if(i<sz-1) f[v]=(f[v]+1ll*f[u]*pre[u][i+1]%mod)%mod;
            else f[v]=(f[v]+f[u])%mod;
        }
    }
    return;
}
void solve(){
    for(int i=1;i<=n;i++) a[i]=1ll*a[i]*pre[m][0]%mod;
    for(int i=1;i<=m;i++)
        if(t[i]==1) a[id[i]]=(a[id[i]]+1ll*f[i]*val[i]%mod)%mod;
    for(int i=1;i<=n;i++) printf("%d ",(a[i]+mod)%mod);
    puts("");
    return;
}
int main(){
    init(); dfs(m);
    topo(); solve();
    return 0;
}

T4

贪吃蛇。

有很多蛇 其中每轮最大的蛇吃掉最小的蛇 每条蛇都在自己不被吃的情况下希望吃尽量多的蛇。

暴力:
set维护n轮 最后扫一遍。 70pts。考场没想出来 还写错爆搜保灵了 自闭。

\(O(Tnlog n)\)

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, lst[N], a[N], ds[N], sj[N];
struct pos {
    int id, val;
    bool operator<(const pos x) const { return val > x.val || (val == x.val && id > x.id); }
};
multiset<pos> s;
int bmin(int x, int y) { return (x > y) ? y : x; }
int main() {
  //  freopen("snakes.in", "r", stdin);
  //  freopen("snakes.out", "w", stdout);

    int t;
    scanf("%d", &t);
    for (int cas = 1; cas <= t; cas++) {
        if (cas == 1) {
            scanf("%d", &n);
            for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        } else {
            for (int i = 1; i <= n; i++) a[i] = lst[i];
            int k;
            scanf("%d", &k);
            for (int i = 1, x, y; i <= k; i++) {
                scanf("%d%d", &x, &y);
                a[x] = y;
            }
        }
        
        s.clear(); memset(ds,0,sizeof(0)); memset(sj,0,sizeof(sj));
        for (int i = 1; i <= n; i++) s.insert((pos){ i, a[i] }), lst[i] = a[i];
        int step = 0;
        for(int i = 1; i < n; i++) {
            int it = (*s.begin()).id, it2 = (*(--s.end())).id;
            s.erase(s.begin()); s.erase(--s.end());
            a[it]-=a[it2]; s.insert((pos){it,a[it]});
            ds[i]=it; sj[it2]=i;
        }
        int it = (*s.begin()).id; s.erase(s.begin());
        sj[it]=n;
        int cnt = n;
        for(int i = n-1; i >= 1; i--)
            if(sj[ds[i]]<cnt) cnt=i;
        //第i轮的大蛇被吃的时间比现在的最早喊停要早,显然会在第i轮喊停。
        printf("%d\n", n-(cnt-1));
    }
    return 0;
}
/*
13 31 33 39 42
7  10 24 48 50
   10 24 48 43
      24 38 43
此时5知道自己再吃会被吃 所以会选择不吃。
         38 29
         9
*/

正解
CSP-S2 贪吃蛇

情况一:

当最强蛇吃了最弱蛇之后,不变成最弱蛇。一定会吃

证明,

  • 如果最强蛇不会跌下巅峰,肯定会吃

  • 在最强蛇跌下巅峰之后,次强蛇开始吃。

    如果次强蛇跌下巅峰之后,因为\(a_n-a_1\ge a_{n-1}-a_2\),一定比最强蛇弱,

    次强蛇一定先被吃,显然它不会把自己搞死,所以最强蛇是安全的。

情况二:

当前最强蛇A吃了最弱蛇之后,变成了最弱蛇A,A会命悬一线,次强蛇B晋级成为最强蛇。

A思考了一下,如果B吃A,那么他这轮就不吃,否则他就吃。他幻想了一下吃会怎么样。

这时,B正准备享用A。

  • B吃完A之后不是最弱蛇,那B一定吃,所以A会死。A为了保命,会选择在上一轮不吃。

  • B吃完A之后是最弱蛇。B也命悬一线。它也幻想了一下,如果C吃。此时次强蛇C晋级了。

    ​ C同样也经历了B一样的过程…

这样模拟一下。

那么什么时候结束呢?

设B是第1条,B是第2条

有两种结束可能。

  1. 到第x条蛇的时候结束,它吃完之后不是最弱蛇,显然会打算吃。

  2. 到第x条蛇的时候结束,它吃完之后只剩一条蛇,也就是说,第x条蛇吃之前有两条蛇。这个时候它显然会选择吃。

    x打算吃,x-1打算不吃,x-2吃,x-3不吃……

    如果x是奇数,B打算吃,所以A会决定不吃。x如果不是奇数,B打算不吃,所以A会决定吃。


    在一直属于情况一的时候,我们会放心地吃。

    当第一条最强蛇开始变成最弱蛇的时候,我们直接进入《A的幻想》 就可以了。

    set维护最强蛇和最弱蛇可以获得70分。

    可以使用类似 蚯蚓-sol 的方法解决

    维护两个双端队列q1,q2.

    队列内单调不降。

    初始将所有东西丢进q1。

    最强蛇:两个队列队尾选一个。

    最弱蛇:q1队首。

    没进《A的幻想》:

    直接在q2头部插入吃过别的蛇的蛇。

    (根据之前的证明,它比别的“吃过别的蛇的蛇”要弱)

    (因为他没进《A的幻想》,所以显然不是最弱蛇)

    进了《A的幻想》:

    把A挖出来作为nw,nw始终表示最弱蛇。

    每轮找最强蛇(q1和q2队尾选一个),然后让他模拟去吃掉nw,更新nw为此时的最强蛇。

    如果只剩两条蛇或者nw不会变成最弱蛇,那么就结束了。

    如何判断是否进入A的幻想?

    q1空了或者nw已经比最弱蛇(q1队首)要小。

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e6 + 10;
    int n, lst[N], snk[N];
    struct pos {
        int id, val;
        bool operator<(const pos x) const { return val < x.val || (val == x.val && id < x.id); }
    };
    deque<pos> q1, q2;
    int bmin(int x, int y) { return (x > y) ? y : x; }
    void init(int cas) {
        if (cas == 1) {
            scanf("%d", &n);
            for (int i = 1; i <= n; i++) scanf("%d", &snk[i]);
        } else {
            for (int i = 1; i <= n; i++) snk[i] = lst[i];
            int k;
            scanf("%d", &k);
            for (int i = 1, x, y; i <= k; i++) {
                scanf("%d%d", &x, &y);
                snk[x] = y;
            }
        }
    }
    void solve() {
        q1.clear();
        q2.clear();
        for (int i = 1; i <= n; i++) q1.push_back((pos){ i, snk[i] }), lst[i] = snk[i];
        int ans;
        while (1) {
            if (q1.size() + q2.size() == 2) {  //只剩下两条蛇
                ans = 1;
                break;
            }
            pos x, y = q1.front();
            q1.pop_front();  //最弱蛇
            if (q2.empty() || !q1.empty() && q2.back() < q1.back()) {
                x = q1.back();
                q1.pop_back();
            } else {
                x = q2.back();
                q2.pop_back();
            }
            snk[x.id] -= snk[y.id];
            pos nw = (pos){ x.id, snk[x.id] };
            if (q1.empty() || nw < q1.front()) {//最弱
                int cnt = 0;
                ans = q1.size() + q2.size() + 2;
                while (1) {
                    cnt++;
                    if (q1.size() + q2.size() + 1 == 2) {
                        if (cnt % 2 == 0)
                            ans--;
                        break;
                    }
                    pos z;
                    if (q2.empty() || !q1.empty() && q2.back() < q1.back()) {
                        z = q1.back();
                        q1.pop_back();
                    } else {
                        z = q2.back();
                        q2.pop_back();
                    }
                    snk[z.id] -= snk[nw.id];
                    nw = (pos){ z.id, snk[z.id] };
                    if ((q1.empty() || nw < q1.front()) && (q2.empty() || nw < q2.front()))
                        ;
                    else {
                        if (cnt % 2 == 0)
                            ans--;
                        break;
                    }
                }
                break;
            } else {
                q2.push_front(nw);
            }
        }
        printf("%d\n", ans);
    }
    int main() {
     //   freopen("snakes.in", "r", stdin);
     //   freopen("snakes.out", "w", stdout);
        int t;
        scanf("%d", &t);
        for (int cas = 1; cas <= t; cas++) {
            init(cas);
            solve();
        }
        return 0;
    }
    /*
    13 31 33 39 42
    
    7  10 24 48 50
       10 24 48 43
          24 38 43
    此时5知道自己再吃会被吃 所以会选择不吃。
             38 19
             19
    */
    
posted @ 2020-11-10 20:13  ACwisher  阅读(402)  评论(0)    收藏  举报