T1T_1T1——nim(3833)
Description:
有nnn堆石子,第iii堆的石子个数为iii,两人可以每次可以在其中一堆中拿任意数量的石子,无法再继续拿为败。求必胜的方案数。
n≤101000n\le 10^{1000}n≤101000
Solution:
- 由于出题人的疯狂,强行练一练高精度,
- 此题可以说是博弈题,也可以说是找规律题(
你甚至可以在大样例中找规律) - 具体的规律是:
- 若nnn为偶数,答案为n−2mx+1n-2^{mx}+1n−2mx+1;
- 若nnn为奇数且(n+1)2\frac{(n+1)}{2}2(n+1)也为奇数时,答案为(n+1)2\frac{(n+1)}{2}2(n+1)。
- 待证明
Code:
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t)for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t)for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t)for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define ll long long
#define N 1002
#define L 255
#define P 10000
struct Big{
int num[L],len;
Big(){
memset(num,0,sizeof(num));
len=1;
}
void Rd(){
char A[N];
scanf("%s",A);
int SL=strlen(A);
len=0;
for(int i=SL-1,res;res=0,i>=0;num[len++]=res,i-=4){
if(i>=3)for(int j=i-3;j<=i;j++)res=(res<<1)+(res<<3)+(A[j]^48);
else for(int j=0;j<=i;j++)res=(res<<1)+(res<<3)+(A[j]^48);
}
}
void Pr(){
if(!num[len-1]){putchar('0');return;}
printf("%d",num[len-1]);
DREP(i,len-2,0)printf("%04d",num[i]);
}
Big operator +(const Big &a)const{
Big b;
b.len=max(len,a.len);
REP(i,0,b.len-1){
int &B=b.num[i];
B+=num[i]+a.num[i];
if(B>=P)B-=P,b.num[i+1]++;
}
if(b.num[b.len])b.len++;
return b;
}
Big operator+(int a){
Big b;
b.len=len;
REP(i,0,len-1){
int &B=b.num[i];
B+=num[i]+a%P;
if(B>=P)b.num[i+1]++,B-=P;
a/=P;
}
if(b.num[b.len])b.len++;
return b;
}
Big operator -(const Big &a)const{
Big b;
REP(i,0,len-1)b.num[i]=num[i];
b.len=len;
DREP(i,a.len-1,0){
b.num[i]-=a.num[i];
int now=i;
while(b.num[now]<0)b.num[now]+=P,now++,b.num[now]--;
}
while(b.len>1 && !b.num[b.len-1])b.len--;
return b;
}
Big operator *(const int &a)const{
Big b;
b.len=len;
REP(i,0,len-1){
int &B=b.num[i];
B+=num[i]*a;
if(B>=P)b.num[i+1]+=B/P,B%=P;
}
if(b.num[b.len])b.len++;
return b;
}
Big operator /(const int &a)const{
Big b;
REP(i,0,len-1)b.num[i]=num[i];
DREP(i,len-1,1)b.num[i-1]+=b.num[i]%a*P,b.num[i]/=a;
b.num[0]/=a;
b.len=len;
while(b.len>1 && !b.num[b.len-1])b.len--;
return b;
}
bool operator<=(const Big &a)const{
if(len!=a.len)return len<a.len;
DREP(i,len-1,0) if(num[i]!=a.num[i]) return num[i]<a.num[i];
return 1;
}
};
int main(){
// freopen("nim.in","r",stdin);
// freopen("nim.out","w",stdout);
int cas;cin>>cas;
Big n;
while(cas--){
n.Rd();
if(n.num[0]&1){
n=n+1;
n=n/2;
if(n.num[0]&1)n.Pr();
else putchar('0');
}
else {
Big mx;
mx=mx+1;
while(mx<=n)mx=mx*2;
mx=mx/2;
n=n-mx+1;
n.Pr();
}
puts("");
}
return 0;
}
T2T_2T2——segment(3056)
Description:
一个长度为nnn的序列AAA,现在想求出有多少对区间[a,b][a,b][a,b]与区间[c,d][c,d][c,d],满足不存在一个数在{Aa,Aa+1,...,Ab−1,Ab}\{A_a,A_{a+1},...,A_{b-1},A_b\}{Aa,Aa+1,...,Ab−1,Ab}和{Ac,Ac+1,...,Ad−1,Ad}\{A_c,A_{c+1},...,A_{d-1},A_d\}{Ac,Ac+1,...,Ad−1,Ad}中同时出现。
n≤3000,1≤Ai≤109n\le3000,1\le A_i\le 10^9n≤3000,1≤Ai≤109
Solution:
Code:
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t)for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t)for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t)for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define ll long long
const int N=3002;
int n;
int A[N];
struct p30{
map<int,int>mp;
void solve(){
int ans=0;
REP(a,1,n) REP(b,a,n) {
mp.clear();
REP(i,a,b) mp[A[i]]++;
REP(c,b+1,n) REP(d,c,n){
bool flag=1;
REP(j,c,d) if(mp[A[j]]){flag=0;break;}
ans+=flag;
}
}
printf("%d\n",ans);
}
}p1;
struct p100{
int tmp[N];
vector<int>pos[N];
int cnt[N];
int pre[N],nxt[N];
int val[N];
void solve(){
memcpy(tmp,A,sizeof(tmp));
sort(tmp+1,tmp+1+n);
REP(i,1,n) {
A[i]=upper_bound(tmp+1,tmp+1+n,A[i])-tmp-1;
pos[A[i]].push_back(i);
}
REP(i,1,n) val[i]=i*(i+1)/2;
ll ans=0;
REP(i,2,n){
REP(j,1,n) cnt[j]=0;
REP(j,i,n) cnt[A[j]]++;
int last=0;
REP(j,1,i) if(cnt[A[j]]){
nxt[last]=j;
pre[j]=last;
last=j;
}
ll sum=0;
for(int j=0;j<i;j=nxt[j]) sum+=val[nxt[j]-j-1];
ans+=sum;
DREP(j,n,i+1) {
--cnt[A[j]];
if(!cnt[A[j]]){
SREP(k,0,pos[A[j]].size()){
int x=pos[A[j]][k];
if(x>=i)break;
int L=pre[x],R=nxt[x];
sum-=val[R-x-1];
sum-=val[x-L-1];
pre[R]=L,nxt[L]=R;
sum+=val[R-L-1];
}
}
ans+=sum;
}
}
printf("%lld\n",ans);
}
}p2;
int main(){
// freopen("segment.in","r",stdin);
// freopen("segment.out","w",stdout);
scanf("%d",&n);
REP(i,1,n) scanf("%d",&A[i]);
if(n<=20)p1.solve();
else p2.solve();
return 0;
}
T3T_3T3——network(3834)
Description:
NOI 2006 网络收费
Solution:
Code:
#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t)for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t)for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define ll long long
template<class T>inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
const int N=(1<<11)+2,INF=0x3f3f3f3f;
int n,m;
int T[N],C[N],F[N][N];
int cost[N][N];
int dp[N][N];
int Pow[12];
int Lca(int x,int y){
while(x!=y)x>>=1,y>>=1;
return x;
}
void solve(int p,int dep){
if(!dep){
dp[p][T[p]]=0,dp[p][!T[p]]=C[p];
for(int i=p>>1;i;i>>=1) dp[p][T[i]]+=cost[i][p];
return;
}
memset(dp[p],63,sizeof(dp[p]));
T[p]=0;
solve(p<<1,dep-1),solve(p<<1|1,dep-1);
REP(i,1,Pow[dep-1]) REP(j,Pow[dep-1]-i+1,Pow[dep]-i) chkmin(dp[p][i+j],dp[p<<1][i]+dp[p<<1|1][j]);
T[p]=1;
solve(p<<1,dep-1),solve(p<<1|1,dep-1);
REP(i,0,Pow[dep-1]) REP(j,0,Pow[dep-1]-i) chkmin(dp[p][i+j],dp[p<<1][i]+dp[p<<1|1][j]);
}
int main(){
// freopen("network.in","r",stdin);
// freopen("network.out","w",stdout);
scanf("%d",&n);
Pow[0]=1;
REP(i,1,10) Pow[i]=Pow[i-1]+Pow[i-1];
REP(i,1,Pow[n]) scanf("%d",&T[i+Pow[n]-1]);
REP(i,1,Pow[n]) scanf("%d",&C[i+Pow[n]-1]);
SREP(i,1,Pow[n]) REP(j,i+1,Pow[n]){
int x=i+Pow[n]-1,y=j+Pow[n]-1;
scanf("%d",&F[x][y]);
int lca=Lca(x,y);
cost[lca][x]+=F[x][y],cost[lca][y]+=F[x][y];
}
solve(1,n);
int ans=INF;
REP(i,0,Pow[n]) chkmin(ans,dp[1][i]);
printf("%d\n",ans);
return 0;
}