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条
有两种结束可能。
-
到第x条蛇的时候结束,它吃完之后不是最弱蛇,显然会打算吃。
-
到第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 */

浙公网安备 33010602011771号