感謝EOJ網站沒承住一千人的壓力,增加了半個小時的比賽時間,使得我成功三題,苟了一個rk10。B題感覺屬實太難,就不補了。賽後補了道D題。
A. 迴文時間
題意:問從2020年1月22日的10:02:02之後第k個迴文時間串是什麼。迴文時間串的定義是年月日小時分鐘秒組成的字符串爲迴文串。起始時間的時間串爲(20200122100202)符合迴文時間串的定義。
題解:暴力就完事了。觀察到因爲是迴文串,所以只要暴力年月日就可以確定後面的時分秒,check一下是否合法就行。
eg:某次從學校對面網吧回宿舍,一個人走在宿舍的路上,我也想到了這道題,後來隨便腦補了一個暴力法就可以過了。結果碰到了一樣的題之後卻因爲前兩發沒有寫十一月的日期白白wa了。
//太暴躁了 寫得好醜
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
ll f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return f*x;
}
int k,f[30],ans=-1;
char s[10];
bool flag(int x){
if(((x%4==0) && (x%100!=0)) || x%400==0)return true;
return false;
}
bool check(int x,int y,int z){
if(z%10!=z/10)return false;
if(y%10*10+y/10>=24)return false;
if(x/1000+(x-(x/1000)*1000)/100*10>=60)return false;
if(x%10*10+x%100/10>=60)return false;
return true;
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("1.txt","w",stdout);
cin>>k;
f[1]=31;f[3]=31;f[5]=31;f[7]=31;f[8]=31;f[10]=31;f[12]=31;
f[4]=30;f[6]=30;f[9]=30;f[11]=30;
for(int i=2020;i<=9999;i++)
for(int j=1;j<=12;j++){
if(flag(i))f[2]=29;
else f[2]=28;
int st=1;
if(i==2020 && j==1)st=22;
for(int l=st;l<=f[j];l++){
if(check(i,j,l))ans++;
if(ans==k){
printf("%d",i);
if(j/10==0)printf("0%d",j);
else printf("%d",j);
if(l/10==0)printf("0%d",l);
else printf("%d",l);
int t=j;
for(int ii=0;ii<2;ii++){
s[ii]=t%10+'0';
t/=10;
cout<<s[ii];
}
t=i;
for(int ii=0;ii<4;ii++){
s[ii]=t%10+'0';
t/=10;
cout<<s[ii];
}
cout<<endl;
return 0;
}
}
}
return 0;
}
E. 數的變幻
題意:給出一個正整數,如果是奇數變成,如果是偶數變成,直到變成1。這個過程會形成一個序列。從1~n會有n個不同的序列,其中數字也會重複的出現。問出現k次的最大的數字是什麼數字。
題解:觀察發現,奇數和偶數分別滿足單調性。所以考慮對奇數和偶數分別二分,取兩個二分之後較大的值。對於check部分,假設是已經被操作過的數,可以發現都是可以到達的。我們反向累加上去,如果總和大於等於k,那麼答案合法,否則不合法。注意處理一下邊界即可
eg:某次cf原題,感謝czb聚聚的傾囊相授,不然肯定不會出的這麼快。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
ll read(){
ll f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return f*x;
}
ll n,k;
int check(ll x){
ll l=x,r=x;
if(x%2==0)r++;
ll ans=0;
while(l<=n){
ans+=min(n,r)-l+1;
l*=2;r=r*2+1;
}
return ans>=k;
}
int main(){
int T;
cin>>T;
while(T--){
scanf("%lld%lld",&n,&k);
ll l=1,r=(n+1)/2,ans=0,mid;
while(l<=r){
mid=(l+r)>>1;
if(check(2*mid-1))l=mid+1,ans=2*mid-1;
else r=mid-1;
}
l=1,r=n/2;
while(l<=r){
mid=(l+r)>>1;
if(check(2*mid))l=mid+1,ans=max(ans,2*mid);
else r=mid-1;
}
printf("%lld\n",ans);
}
return 0;
}
C. 最簡邊集
題意:給出一個有向無環圖,在保持連通性的情況下儘量刪邊,問最多能刪多少邊。
題解:有向無環圖,大力拓撲排序。按照拓撲序反向加邊,如果不改變聯通性就不加邊。用bitset直接相或,可以維護聯通性。
eg:bzoj原題,一開始wa3,重構之後tle21,加上了玄學優化們,才終於過了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e4+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
inline int read(){
int f=1,x=0;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
return f*x;
}
int n,m,cnt,num,ans;
bitset<maxn>f[maxn];
struct arr{
int next,to;
}edge[50010];
int head[maxn],in[maxn],a[maxn],b[maxn],to[maxn];
inline bool cmp(int x,int y){
return b[x]<b[y];
}
inline void add(int x,int y){
cnt++;
edge[cnt].to=y;edge[cnt].next=head[x];
head[x]=cnt;
}
inline void topusort(){
queue<int>q;
for(int i=1;i<=n;i++)
if(!in[i])q.push(i);
while(!q.empty()){
int x=q.front();q.pop();
a[++num]=x;//第幾個入隊的是誰
b[x]=num;//誰第幾個入隊
for(int i=head[x];i!=-1;i=edge[i].next){
in[edge[i].to]--;
if(!in[edge[i].to])q.push(edge[i].to);
}
}
}
inline void solve(){
for(int i=n;i>=1;i--){
int x=a[i],tot=0;
f[x][x]=1;
for(int j=head[x];j!=-1;j=edge[j].next){
to[++tot]=edge[j].to;
}
sort(to+1,to+1+tot,cmp);
for(int j=1;j<=tot;j++)
if(f[x][to[j]])ans++;
else f[x]|=f[to[j]];
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
memset(head,-1,sizeof(head));
cnt=0;num=0;ans=0;
n=read();m=read();
for(int i=1;i<=n;i++)f[i].reset();
for(int i=1;i<=m;i++){
int x,y;
x=read();y=read();
add(x,y);
in[y]++;
}
topusort();
solve();
printf("%d\n",ans);
}
return 0;
}
D. 對稱變量
題意:給出一個n元二次多項式,其中有m個二次項(係數都爲1且都是由乘積構成),可能存在平方項。對稱變量的定義是,交換兩個變量的所有位置,如果還是等價的則是一對對稱變量。問有多少對對稱變量。
題解:考慮兩個變量,有兩種情況。一、兩變量之間有乘積項,則兩變量的哈希值去掉對方所表示的哈希值之後應該相等。二、兩變量之間沒有乘積項,則其對應的哈希值應該相等。此外,含平方項的應該單獨處理。
eg:比賽的時候以爲是一道圖論題,賽後問了學弟才發現是哈希。哈希還是值得好好研究研究啊QAQ。
#include "stdio.h"
#include "string.h"
#include "iostream"
#include "algorithm"
#include "vector"
#include "math.h"
#include "map"
#include "set"
using namespace std;
typedef long long ll;
const int maxn=5e5+10;
const int INF=0x3f3f3f;
const int mod=1e9+7;
int n,m;
ll h[maxn];
int x[maxn],y[maxn];
struct arr{
ll h;
bool flag;
}a[maxn],b[maxn];
void gethash(){
h[0]=233;
for(int i=1;i<maxn;i++)h[i]=h[i-1]*1003;
}
bool cmp(arr a,arr b){
return a.h<b.h;
}
int main(){
gethash();
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++)a[i].h=0,b[i].h=0;
ll ans=0;
for(int i=1;i<=m;i++){
scanf("%d%d",&x[i],&y[i]);
a[x[i]].h+=h[y[i]];
if(x[i]==y[i])a[x[i]].flag=1;
else a[y[i]].h+=h[x[i]];
}
for(int i=1;i<=m;i++)
if(x[i]==y[i])continue;
else if(a[x[i]].h-h[y[i]]==a[y[i]].h-h[x[i]])ans++;
int pos=0;ll sum=1;
for(int i=1;i<=n;i++)
if(a[i].flag)b[++pos].h=a[i].h-h[i];
sort(a+1,a+1+n,cmp);
for(int i=2;i<=n;i++)
if(a[i].h==a[i-1].h)sum++;
else ans+=sum*(sum-1)/2,sum=1;
ans+=sum*(sum-1)/2;sum=1;
sort(b+1,b+1+pos,cmp);
for(int i=2;i<=pos;i++)
if(b[i].h==b[i-1].h)sum++;
else ans+=sum*(sum-1)/2,sum=1;
ans+=sum*(sum-1)/2;
printf("%lld\n",ans);
}
return 0;
}
總結
希望以後成爲所有題都能補的強者!!