19-11-09-∵
先挖个坑,不然多半会忘掉
关于对拍?
大家都是怎么写的呀?
$\text{vim}$玄手又来发广告了。
写下面这个,不用再看一眼对拍:
if(system("diff a.out b.out -b -B -q")){ puts("WA"); system("gnome-terminal --command=\"vimdiff a.out b.out\""); system("gnome-terminal --command=\"vim 1.in\""); return 0; }
只要$\text{wa}$了就突然钻出一个光头弹出两个窗口,就是用$\text{vim}$打开那三个东西,$\text{gedit}$玩家请自行修改。
总结:
T1一看是个$\Theta(N^2)$题。
emm。于是先码一个暴力,然后接着一个贪心。
疯狂对拍$\color{forestgreen}{\text{AC}}$
T2不是很清楚怎么做,然后先写暴力。
T3……写了一个特判,然后就是一大堆测试点分治(可以这很NOIPCSP-S)
状态还行。
27
|
Miemeng | 100
00:00:33
|
40
00:00:34
|
55
00:00:34
|
195
00:00:34
|
TJ解:
T1:
我的考试方法仿佛伪了……
我当时是枚举中间点直接暴力向左右扫并将相同的合并过来,
所以因为是直接判的长度而不是长度加前缀和,于是有时候不对。
但是因为枚举了不同的$\text{mid}$所以还不能卡掉。
所以它现在还处在半A不A的状态(///▽///)
//swap #include <iostream> #include <cstring> #include <cstdio> #define N 4444 using namespace std; int len,cn; char str[N]; int ans=0; int getmid(int mid){ int lft=cn,gn=1,lpre=0,rpre=0; for(int i=1;i<=len;i++){ if(lft<i-lpre-1 && lft<i-rpre-1)break; if(mid+i>len && mid-i<1)break; if(mid+i<=len && lft>=i-lpre-1 && str[mid+i]==str[mid]){ lft-=(i-lpre-1); lpre++; gn++; } if(mid-i>=1 && lft>=i-rpre-1 && str[mid-i]==str[mid]){ lft-=(i-rpre-1); rpre++; gn++; } } return gn; } int main(){ #ifndef LOCAL freopen("swap.in" ,"r",stdin); freopen("swap.out","w",stdout); #endif scanf("%d%d%s",&len,&cn,str+1); for(int i=1;i<=len;i++){ // cout<<i<<" "<<getmid(i)<<endl; ans=max(getmid(i),ans); } cout<<ans<<endl; }
T2
好久没有改完题的快感了……
这个题还是非常可做的qwq
首先我们先码一个暴力。
pua!
$\text{20%}$算法
直接暴力即可。
你可以像这样判断一个数是否是完全平方数。
bool is_sq(LL val){ double sq=sqrt(val); return floor(sq)==ceil(sq); }
然后你就被卡精了(滑稽
发现精度不够……
bool is_sq(LL val){ long double sq=sqrt(val); return floor(sq)==ceil(sq); }
好了$1\text{~}10^{18}$都正确了。
但是还有一种更有脑子的做法。
bool is_p2(int va){ int qva=sqrt(va); return qva*qva==va; }
另$\text{20%}$算法
我们发现有些数很小。
$a_i \leq 1000$
开桶维护即可。
- 二合一:
//square #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <cmath> #define N 333333 #define sqA 31632 #define LL long long using namespace std; int nn; LL arr[N]; int ans=0; bool is_sq(LL val){ long double sq=sqrt(val); // if(floor(sq)==ceil(sq))cout<<sq<<endl; return floor(sq)==ceil(sq); } namespace _3{ const int T_Size=1111; int tb[T_Size],sqr[T_Size]; bool mp[T_Size][T_Size]; void prerun(){ memset(tb,0,sizeof tb); for(int i=1;i<=1000;i++){ sqr[i]=i*i; } for(int i=1;i<=1000;i++){ for(int j=1;j<=1000;j++){ if(sqr[i]%j==0 && sqr[i]/j<=1000){ mp[sqr[i]/j][j]=mp[j][sqr[i]/j]=1; } } } } void work(){ prerun(); for(int i=1;i<=nn;i++) tb[arr[i]]++; for(int i=1;i<=1000;i++){ if(tb[i]==0)continue; for(int j=1;j<=1000;j++){ if(mp[i][j]==1){ // cout<<i<<" "<<j<<" "<<tb[i]*tb[j]<<endl; if(i!=j) ans+=tb[i]*tb[j]; else ans+=tb[i]*(tb[j]-1); } } } cout<<ans/2<<endl; } } int main(){ #ifndef LOCAL freopen("square.in" ,"r",stdin); freopen("square.out","w",stdout); #endif bool allsm=1; scanf("%d",&nn); for(int i=1;i<=nn;i++){ scanf("%lld",arr+i); if(arr[i]>1000)allsm=0; } if(allsm)_3::work(); else{ for(int i=1;i<=nn;i++){ for(int j=i+1;j<=nn;j++){ if(is_sq(arr[i]*arr[j])){ ans++; } } } cout<<ans<<endl; } }
$\text{70%}$算法
蒟蒻的思维可能到不了这里??(为什么想了却扔掉了啊?
我们发现如果某个数有平方因子,那是没有意义的……
于是求$\sqrt{\max\{a_i\}}$内的质数。
对每个数进行处理,然后排序统计相同的即可。
如果你开桶($\text{map}$)好像会被卡常。
这题还是挺卡常的……
所以少开long long!
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <cmath> #define N 333333 #define SQ 31633 using namespace std; int nn,arr[N]; bool notp[SQ]; vector <int> pr; int ans=0; void getprime(const int lim){ notp[1]=1; for(int i=2;i<=lim;i++){ if(!notp[i]){ pr.push_back(i); for(int j=i*2;j<=lim;j+=i){ notp[j]=1; } } } } int main(){ #ifndef LOCAL freopen("square.in" ,"r",stdin); freopen("square.out","w",stdout); #endif int maxn=0; cin.sync_with_stdio(false); cin>>nn; for(int i=1;i<=nn;i++){ cin>>arr[i]; maxn=max(maxn,arr[i]); } getprime(sqrt(maxn)+1); for(int i=1;i<=nn;i++){ int dat=arr[i]; for(int j=0;j<int(pr.size());j++){ while(dat%(pr[j]*pr[j])==0) dat/=(pr[j]*pr[j]); if(dat==1)break; } // cout<<arr[i]<<" "<<dat<<endl; arr[i]=dat; } int val=1; sort(arr+1,arr+nn+1); for(int i=1;i<=nn;i++){ // cout<<arr[i]<<"V"<<val<<endl; if(arr[i]==arr[i-1]){ val++; } else{ ans+=val*(val-1)/2; val=1; } // cout<<arr[i]<<"^"<<val<<endl; } ans+=val*(val-1)/2; cout<<ans<<endl; #ifndef LOCAL fclose(stdin); fclose(stdout); #endif }
$\text{100%}$算法
上面的过程非常优秀了,但是由于$\sqrt{\max\{a_i\}}$还是太巨了,于是还是只有$70$
我们就想简化这个过程,
一个数最多只有一个大于$\sqrt[3]{\max\{a_i\}}$的成平方的因子
那么我们只要想办法解决上面的因子就好了。
于是先用$\sqrt[3]{\max\{a_i\}}$的因子去筛,剩下的判断是否是完全平方数就可以快速处理。
具体细节见代码:
#include <algorithm> #include <iostream> #include <cstdio> #include <vector> #include <cmath> #define SQ3 1211 #define N 333333 using namespace std; int nn,arr[N]; bool notp[SQ3]; vector <int> pr; long long ans=0; void getprime(const int lim){ notp[1]=1; for(int i=2;i<=lim;i++){ if(!notp[i]){ pr.push_back(i); for(int j=i*2;j<=lim;j+=i){ notp[j]=1; } } } } bool is_p2(int va){ int qva=sqrt(va); return qva*qva==va; } int main(){ #ifndef LOCAL freopen("square.in" ,"r",stdin); freopen("square.out","w",stdout); #endif cin.sync_with_stdio(false); int maxn=0; cin>>nn; for(int i=1;i<=nn;i++){ cin>>arr[i]; maxn=max(maxn,arr[i]); } getprime(1100); for(int i=1;i<=nn;i++){ int dat=arr[i]; for(int j=0;j<int(pr.size());j++){ while(arr[i]%(pr[j]*pr[j])==0) arr[i]/=pr[j]*pr[j]; while(dat%pr[j]==0) dat/=pr[j]; } // cout<<dat<<" "<<arr[i]<<endl; if(is_p2(dat)) arr[i]/=dat; } sort(arr+1,arr+nn+1); int val=1; for(int i=1;i<=nn;i++){ if(arr[i]==arr[i-1])val++; else{ ans+=1ll*val*(val-1)/2; val=1; } } ans+=1ll*val*(val-1)/2; cout<<ans<<endl; #ifndef LOCAL fclose(stdin); fclose(stdout); #endif }
T3
好dp题。
那么就好好写个dp柿子。
为了找到合法路径我们可以搞一下非法路径。
于是设$f_i$为以$i$为起点的路径数,记$ways(i,j)$为从$i$到$j$的路径数。
$$f_i=ways(A,i)-\sum \limits_{j}^{j \rightarrow i} f_j \times ways(i,j)$$
简单容斥即可。
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #define N 111111 #define D 111 #define LL long long #define aim(k) po[pn+1][(k)] using namespace std; const int Mod=1e9+7, Phi=Mod-1; int dn,pn; struct POINT{ int arr[D]; int & operator [] (const int id){ return this->arr[id]; } friend bool operator < (const POINT &a,const POINT &b){ for(int i=1;i<=dn;i++){ if(a.arr[i] != b.arr[i]) return a.arr[i] < b.arr[i]; } return 1; } }po[N]; int fac[N*D],inv[N*D]; int dp[N]; LL ans=0; LL ppow(LL a,LL b){ a%=Mod; b%=Phi; LL res=1; while(b){ if(b&1)res=res*a%Mod; a=a*a%Mod; b>>=1; } return res; } void prerun(){ int lim=10000000; fac[0]=inv[0]=1; for(int i=1;i<=lim;i++) fac[i]=1ll*fac[i-1]*i%Mod; inv[lim]=ppow(fac[lim],Mod-2); for(int i=lim-1;i>=1;i--){ inv[i]=1ll*inv[i+1]*(i+1)%Mod; } } LL C(LL n,LL m){ if(n<m)return 0; if(m<0 || n<0) return 0; return 1ll*fac[n]*inv[m]%Mod*inv[n-m]%Mod; } LL getways(int a,int b){ int sum=0; LL dat=1; for(int i=1;i<=dn;i++) sum+=po[a][i]-po[b][i]; for(int i=1;i<=dn;i++){ dat=dat*C(sum,po[a][i]-po[b][i])%Mod; sum-=po[a][i]-po[b][i]; } return dat; } int main(){ #ifndef LOCAL freopen("net.in" ,"r",stdin); freopen("net.out","w",stdout); #endif cin.sync_with_stdio(false); prerun(); cin>>dn>>pn; for(int i=1;i<=dn;i++) cin>>aim(i); for(int i=1;i<=pn;i++){ for(int j=1;j<=dn;j++){ cin>>po[i][j]; } } ans=getways(pn+1,0); sort(po+1,po+pn+1); /* for(int i=1;i<=pn;i++){ for(int j=1;j<=dn;j++) cout<<po[i][j]<<" "; cout<<endl; }*/ for(int i=1;i<=pn;i++){ dp[i]=getways(i,0); for(int j=1;j<i;j++){ dp[i]=(1ll*dp[i]-1ll*dp[j]*getways(i,j)%Mod+Mod)%Mod; } } for(int i=1;i<=pn;i++){ // cout<<i<<" "<<dp[i]*getways(pn+1,i)<<endl; ans=(ans-1ll*dp[i]*getways(pn+1,i)%Mod+Mod)%Mod; } cout<<ans<<endl; }